├── .gitignore ├── .readthedocs.yaml ├── LICENSE ├── MANIFEST.in ├── Makefile ├── README.rst ├── docs ├── Makefile ├── conf.py ├── getting-started.rst └── index.rst ├── getwarped.sh ├── openshift └── warpdrive-python.json ├── s2i ├── bin │ ├── assemble │ ├── run │ ├── save-artifacts │ └── usage └── bootstrap │ ├── assemble │ ├── run │ └── save-artifacts ├── setup.py └── warpdrive ├── __init__.py ├── bin └── warpdrive └── etc ├── __init__.py ├── base-debian ├── shell-init ├── shell-rcfile ├── start-gunicorn ├── start-mod_wsgi ├── start-python ├── start-shell ├── start-uwsgi ├── start-waitress ├── warpdrive-activate ├── warpdrive-alive ├── warpdrive-base ├── warpdrive-build ├── warpdrive-destroy ├── warpdrive-enter ├── warpdrive-env ├── warpdrive-exec ├── warpdrive-fixup ├── warpdrive-help ├── warpdrive-image ├── warpdrive-migrate ├── warpdrive-profile ├── warpdrive-rcfile ├── warpdrive-ready ├── warpdrive-setup ├── warpdrive-shell ├── warpdrive-start ├── warpdrive-verify └── warpdrive-wheels /.gitignore: -------------------------------------------------------------------------------- 1 | *.py[cod] 2 | 3 | # C extensions 4 | *.so 5 | 6 | # Packages 7 | *.egg 8 | *.egg-info 9 | dist 10 | build 11 | eggs 12 | parts 13 | bin 14 | var 15 | sdist 16 | develop-eggs 17 | .installed.cfg 18 | lib 19 | lib64 20 | 21 | man 22 | include 23 | .Python 24 | MANIFEST 25 | 26 | # Installer logs 27 | pip-log.txt 28 | 29 | # Unit test 30 | .cache 31 | .tox 32 | nosetests.xml 33 | 34 | # Translations 35 | *.mo 36 | 37 | # Mr Developer 38 | .mr.developer.cfg 39 | .project 40 | .pydevproject 41 | 42 | # Editor save files. 43 | .*.swp 44 | 45 | # Sphinx documentation. 46 | docs/_build 47 | 48 | # Coverage. 49 | htmlcov 50 | .coverage 51 | .coverage.* 52 | .tddium* 53 | -------------------------------------------------------------------------------- /.readthedocs.yaml: -------------------------------------------------------------------------------- 1 | # .readthedocs.yaml 2 | # Read the Docs configuration file 3 | # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details 4 | 5 | # Required 6 | version: 2 7 | 8 | # Set the version of Python and other tools you might need 9 | build: 10 | os: ubuntu-22.04 11 | tools: 12 | python: "3.11" 13 | 14 | # Build documentation in the docs/ directory with Sphinx 15 | sphinx: 16 | configuration: docs/conf.py 17 | 18 | # We recommend specifying your dependencies to enable reproducible builds: 19 | # https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html 20 | # python: 21 | # install: 22 | # - requirements: docs/requirements.txt 23 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016, Graham Dumpleton 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 18 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 19 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 20 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 21 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 22 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 23 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 24 | POSSIBILITY OF SUCH DAMAGE. 25 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include warpdrive/bin/* 2 | include warpdrive/etc/* 3 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY : all 2 | 3 | all : 4 | 5 | install : all 6 | pip install -U . 7 | 8 | package : 9 | python setup.py sdist 10 | 11 | release : clean package 12 | twine upload dist/* 13 | 14 | clean : 15 | rm -rf build dist warpdrive.egg-info 16 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | The ``warpdrive`` project provide the scripts for implementing a build and 2 | deployment system for Python web applications using Docker. The scripts can 3 | be integrated into a suitable Docker base image to provide a more 4 | structured way for incorporating a Python web application into a Docker 5 | image, with ``warpdrive`` doing all the hard work of co-ordinating the 6 | build of the Docker image containing your application. The ``warpdrive`` 7 | scripts will also handle the startup of the Python web application when the 8 | container is run. 9 | 10 | As well as basic support for working with Docker directly, the ``warpdrive`` 11 | project also provides ``assemble`` and ``run`` scripts suitable for use 12 | with the `Source-to-Image`_ (S2I) project. This allows a Docker image to be 13 | enabled as a S2I builder for constructing Docker images for your 14 | application without you needing to even know how to build Docker 15 | containers. Any S2I enabled Docker image would also be able to be used as a 16 | S2I builder with any Docker based PaaS with S2I support built in, such as 17 | OpenShift. 18 | 19 | For additional documentation on ``warpdrive`` including examples of use 20 | see: 21 | 22 | * http://warpdrive.readthedocs.io 23 | 24 | For examples of Docker images which incorporate the ``warpdrive`` scripts 25 | see: 26 | 27 | * `warp0-debian8-python`_ 28 | * `warp0-centos7-python`_ 29 | 30 | If you would like to try ``warpdrive`` with OpenShift, but would like to 31 | use the default OpenShift Python images as a base, templates are provided 32 | which use the standard Python images, but where the existing S2I scripts 33 | will be overridden. To load these templates use the command:: 34 | 35 | oc create -f https://raw.githubusercontent.com/GrahamDumpleton/warpdrive/master/openshift/warpdrive-python.json 36 | 37 | This will create the following templates in OpenShift:: 38 | 39 | warpdrive-python27 40 | warpdrive-python33 41 | warpdrive-python34 42 | warpdrive-python35 43 | 44 | .. _`Source-to-Image`: https://github.com/openshift/source-to-image 45 | .. _`warp0-debian8-python`: https://github.com/GrahamDumpleton/warp0-debian8-python 46 | .. _`warp0-centos7-python`: https://github.com/GrahamDumpleton/warp0-centos7-python 47 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | PAPER = 8 | BUILDDIR = _build 9 | 10 | # User-friendly check for sphinx-build 11 | ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) 12 | $(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) 13 | endif 14 | 15 | # Internal variables. 16 | PAPEROPT_a4 = -D latex_paper_size=a4 17 | PAPEROPT_letter = -D latex_paper_size=letter 18 | ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . 19 | # the i18n builder cannot share the environment and doctrees with the others 20 | I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . 21 | 22 | .PHONY: help 23 | help: 24 | @echo "Please use \`make ' where is one of" 25 | @echo " html to make standalone HTML files" 26 | @echo " dirhtml to make HTML files named index.html in directories" 27 | @echo " singlehtml to make a single large HTML file" 28 | @echo " pickle to make pickle files" 29 | @echo " json to make JSON files" 30 | @echo " htmlhelp to make HTML files and a HTML help project" 31 | @echo " qthelp to make HTML files and a qthelp project" 32 | @echo " applehelp to make an Apple Help Book" 33 | @echo " devhelp to make HTML files and a Devhelp project" 34 | @echo " epub to make an epub" 35 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" 36 | @echo " latexpdf to make LaTeX files and run them through pdflatex" 37 | @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" 38 | @echo " text to make text files" 39 | @echo " man to make manual pages" 40 | @echo " texinfo to make Texinfo files" 41 | @echo " info to make Texinfo files and run them through makeinfo" 42 | @echo " gettext to make PO message catalogs" 43 | @echo " changes to make an overview of all changed/added/deprecated items" 44 | @echo " xml to make Docutils-native XML files" 45 | @echo " pseudoxml to make pseudoxml-XML files for display purposes" 46 | @echo " linkcheck to check all external links for integrity" 47 | @echo " doctest to run all doctests embedded in the documentation (if enabled)" 48 | @echo " coverage to run coverage check of the documentation (if enabled)" 49 | 50 | .PHONY: clean 51 | clean: 52 | rm -rf $(BUILDDIR)/* 53 | 54 | .PHONY: html 55 | html: 56 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html 57 | @echo 58 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." 59 | 60 | .PHONY: dirhtml 61 | dirhtml: 62 | $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml 63 | @echo 64 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." 65 | 66 | .PHONY: singlehtml 67 | singlehtml: 68 | $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml 69 | @echo 70 | @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." 71 | 72 | .PHONY: pickle 73 | pickle: 74 | $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle 75 | @echo 76 | @echo "Build finished; now you can process the pickle files." 77 | 78 | .PHONY: json 79 | json: 80 | $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json 81 | @echo 82 | @echo "Build finished; now you can process the JSON files." 83 | 84 | .PHONY: htmlhelp 85 | htmlhelp: 86 | $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp 87 | @echo 88 | @echo "Build finished; now you can run HTML Help Workshop with the" \ 89 | ".hhp project file in $(BUILDDIR)/htmlhelp." 90 | 91 | .PHONY: qthelp 92 | qthelp: 93 | $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp 94 | @echo 95 | @echo "Build finished; now you can run "qcollectiongenerator" with the" \ 96 | ".qhcp project file in $(BUILDDIR)/qthelp, like this:" 97 | @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/warpdrive.qhcp" 98 | @echo "To view the help file:" 99 | @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/warpdrive.qhc" 100 | 101 | .PHONY: applehelp 102 | applehelp: 103 | $(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp 104 | @echo 105 | @echo "Build finished. The help book is in $(BUILDDIR)/applehelp." 106 | @echo "N.B. You won't be able to view it unless you put it in" \ 107 | "~/Library/Documentation/Help or install it in your application" \ 108 | "bundle." 109 | 110 | .PHONY: devhelp 111 | devhelp: 112 | $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp 113 | @echo 114 | @echo "Build finished." 115 | @echo "To view the help file:" 116 | @echo "# mkdir -p $$HOME/.local/share/devhelp/warpdrive" 117 | @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/warpdrive" 118 | @echo "# devhelp" 119 | 120 | .PHONY: epub 121 | epub: 122 | $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub 123 | @echo 124 | @echo "Build finished. The epub file is in $(BUILDDIR)/epub." 125 | 126 | .PHONY: latex 127 | latex: 128 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 129 | @echo 130 | @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." 131 | @echo "Run \`make' in that directory to run these through (pdf)latex" \ 132 | "(use \`make latexpdf' here to do that automatically)." 133 | 134 | .PHONY: latexpdf 135 | latexpdf: 136 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 137 | @echo "Running LaTeX files through pdflatex..." 138 | $(MAKE) -C $(BUILDDIR)/latex all-pdf 139 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 140 | 141 | .PHONY: latexpdfja 142 | latexpdfja: 143 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 144 | @echo "Running LaTeX files through platex and dvipdfmx..." 145 | $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja 146 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 147 | 148 | .PHONY: text 149 | text: 150 | $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text 151 | @echo 152 | @echo "Build finished. The text files are in $(BUILDDIR)/text." 153 | 154 | .PHONY: man 155 | man: 156 | $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man 157 | @echo 158 | @echo "Build finished. The manual pages are in $(BUILDDIR)/man." 159 | 160 | .PHONY: texinfo 161 | texinfo: 162 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 163 | @echo 164 | @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." 165 | @echo "Run \`make' in that directory to run these through makeinfo" \ 166 | "(use \`make info' here to do that automatically)." 167 | 168 | .PHONY: info 169 | info: 170 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 171 | @echo "Running Texinfo files through makeinfo..." 172 | make -C $(BUILDDIR)/texinfo info 173 | @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." 174 | 175 | .PHONY: gettext 176 | gettext: 177 | $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale 178 | @echo 179 | @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." 180 | 181 | .PHONY: changes 182 | changes: 183 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes 184 | @echo 185 | @echo "The overview file is in $(BUILDDIR)/changes." 186 | 187 | .PHONY: linkcheck 188 | linkcheck: 189 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck 190 | @echo 191 | @echo "Link check complete; look for any errors in the above output " \ 192 | "or in $(BUILDDIR)/linkcheck/output.txt." 193 | 194 | .PHONY: doctest 195 | doctest: 196 | $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest 197 | @echo "Testing of doctests in the sources finished, look at the " \ 198 | "results in $(BUILDDIR)/doctest/output.txt." 199 | 200 | .PHONY: coverage 201 | coverage: 202 | $(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage 203 | @echo "Testing of coverage in the sources finished, look at the " \ 204 | "results in $(BUILDDIR)/coverage/python.txt." 205 | 206 | .PHONY: xml 207 | xml: 208 | $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml 209 | @echo 210 | @echo "Build finished. The XML files are in $(BUILDDIR)/xml." 211 | 212 | .PHONY: pseudoxml 213 | pseudoxml: 214 | $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml 215 | @echo 216 | @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." 217 | -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # warpdrive documentation build configuration file, created by 4 | # sphinx-quickstart on Mon Feb 15 16:13:54 2016. 5 | # 6 | # This file is execfile()d with the current directory set to its 7 | # containing dir. 8 | # 9 | # Note that not all possible configuration values are present in this 10 | # autogenerated file. 11 | # 12 | # All configuration values have a default; values that are commented out 13 | # serve to show the default. 14 | 15 | import sys 16 | import os 17 | 18 | # If extensions (or modules to document with autodoc) are in another directory, 19 | # add these directories to sys.path here. If the directory is relative to the 20 | # documentation root, use os.path.abspath to make it absolute, like shown here. 21 | #sys.path.insert(0, os.path.abspath('.')) 22 | 23 | # -- General configuration ------------------------------------------------ 24 | 25 | # If your documentation needs a minimal Sphinx version, state it here. 26 | #needs_sphinx = '1.0' 27 | 28 | # Add any Sphinx extension module names here, as strings. They can be 29 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 30 | # ones. 31 | extensions = [] 32 | 33 | # Add any paths that contain templates here, relative to this directory. 34 | templates_path = ['_templates'] 35 | 36 | # The suffix(es) of source filenames. 37 | # You can specify multiple suffix as a list of string: 38 | # source_suffix = ['.rst', '.md'] 39 | source_suffix = '.rst' 40 | 41 | # The encoding of source files. 42 | #source_encoding = 'utf-8-sig' 43 | 44 | # The master toctree document. 45 | master_doc = 'index' 46 | 47 | # General information about the project. 48 | project = u'warpdrive' 49 | copyright = u'2016-2020, Graham Dumpleton' 50 | author = u'Graham Dumpleton' 51 | 52 | # The version info for the project you're documenting, acts as replacement for 53 | # |version| and |release|, also used in various other places throughout the 54 | # built documents. 55 | # 56 | # The short X.Y version. 57 | version = u'0.34' 58 | # The full version, including alpha/beta/rc tags. 59 | release = u'0.34.0' 60 | 61 | # The language for content autogenerated by Sphinx. Refer to documentation 62 | # for a list of supported languages. 63 | # 64 | # This is also used if you do content translation via gettext catalogs. 65 | # Usually you set "language" from the command line for these cases. 66 | language = None 67 | 68 | # There are two options for replacing |today|: either, you set today to some 69 | # non-false value, then it is used: 70 | #today = '' 71 | # Else, today_fmt is used as the format for a strftime call. 72 | #today_fmt = '%B %d, %Y' 73 | 74 | # List of patterns, relative to source directory, that match files and 75 | # directories to ignore when looking for source files. 76 | exclude_patterns = ['_build'] 77 | 78 | # The reST default role (used for this markup: `text`) to use for all 79 | # documents. 80 | #default_role = None 81 | 82 | # If true, '()' will be appended to :func: etc. cross-reference text. 83 | #add_function_parentheses = True 84 | 85 | # If true, the current module name will be prepended to all description 86 | # unit titles (such as .. function::). 87 | #add_module_names = True 88 | 89 | # If true, sectionauthor and moduleauthor directives will be shown in the 90 | # output. They are ignored by default. 91 | #show_authors = False 92 | 93 | # The name of the Pygments (syntax highlighting) style to use. 94 | pygments_style = 'sphinx' 95 | 96 | # A list of ignored prefixes for module index sorting. 97 | #modindex_common_prefix = [] 98 | 99 | # If true, keep warnings as "system message" paragraphs in the built documents. 100 | #keep_warnings = False 101 | 102 | # If true, `todo` and `todoList` produce output, else they produce nothing. 103 | todo_include_todos = False 104 | 105 | 106 | # -- Options for HTML output ---------------------------------------------- 107 | 108 | # The theme to use for HTML and HTML Help pages. See the documentation for 109 | # a list of builtin themes. 110 | #html_theme = 'alabaster' 111 | 112 | on_rtd = os.environ.get('READTHEDOCS', None) == 'True' 113 | if on_rtd: 114 | html_theme = 'default' 115 | else: 116 | import sphinx_rtd_theme 117 | html_theme = 'sphinx_rtd_theme' 118 | html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] 119 | 120 | # Theme options are theme-specific and customize the look and feel of a theme 121 | # further. For a list of options available for each theme, see the 122 | # documentation. 123 | #html_theme_options = {} 124 | 125 | # Add any paths that contain custom themes here, relative to this directory. 126 | #html_theme_path = [] 127 | 128 | # The name for this set of Sphinx documents. If None, it defaults to 129 | # " v documentation". 130 | #html_title = None 131 | 132 | # A shorter title for the navigation bar. Default is the same as html_title. 133 | #html_short_title = None 134 | 135 | # The name of an image file (relative to this directory) to place at the top 136 | # of the sidebar. 137 | #html_logo = None 138 | 139 | # The name of an image file (within the static path) to use as favicon of the 140 | # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 141 | # pixels large. 142 | #html_favicon = None 143 | 144 | # Add any paths that contain custom static files (such as style sheets) here, 145 | # relative to this directory. They are copied after the builtin static files, 146 | # so a file named "default.css" will overwrite the builtin "default.css". 147 | html_static_path = ['_static'] 148 | 149 | # Add any extra paths that contain custom files (such as robots.txt or 150 | # .htaccess) here, relative to this directory. These files are copied 151 | # directly to the root of the documentation. 152 | #html_extra_path = [] 153 | 154 | # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, 155 | # using the given strftime format. 156 | #html_last_updated_fmt = '%b %d, %Y' 157 | 158 | # If true, SmartyPants will be used to convert quotes and dashes to 159 | # typographically correct entities. 160 | #html_use_smartypants = True 161 | 162 | # Custom sidebar templates, maps document names to template names. 163 | #html_sidebars = {} 164 | 165 | # Additional templates that should be rendered to pages, maps page names to 166 | # template names. 167 | #html_additional_pages = {} 168 | 169 | # If false, no module index is generated. 170 | #html_domain_indices = True 171 | 172 | # If false, no index is generated. 173 | #html_use_index = True 174 | 175 | # If true, the index is split into individual pages for each letter. 176 | #html_split_index = False 177 | 178 | # If true, links to the reST sources are added to the pages. 179 | #html_show_sourcelink = True 180 | 181 | # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. 182 | #html_show_sphinx = True 183 | 184 | # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. 185 | #html_show_copyright = True 186 | 187 | # If true, an OpenSearch description file will be output, and all pages will 188 | # contain a tag referring to it. The value of this option must be the 189 | # base URL from which the finished HTML is served. 190 | #html_use_opensearch = '' 191 | 192 | # This is the file name suffix for HTML files (e.g. ".xhtml"). 193 | #html_file_suffix = None 194 | 195 | # Language to be used for generating the HTML full-text search index. 196 | # Sphinx supports the following languages: 197 | # 'da', 'de', 'en', 'es', 'fi', 'fr', 'hu', 'it', 'ja' 198 | # 'nl', 'no', 'pt', 'ro', 'ru', 'sv', 'tr' 199 | #html_search_language = 'en' 200 | 201 | # A dictionary with options for the search language support, empty by default. 202 | # Now only 'ja' uses this config value 203 | #html_search_options = {'type': 'default'} 204 | 205 | # The name of a javascript file (relative to the configuration directory) that 206 | # implements a search results scorer. If empty, the default will be used. 207 | #html_search_scorer = 'scorer.js' 208 | 209 | # Output file base name for HTML help builder. 210 | htmlhelp_basename = 'warpdrivedoc' 211 | 212 | # -- Options for LaTeX output --------------------------------------------- 213 | 214 | latex_elements = { 215 | # The paper size ('letterpaper' or 'a4paper'). 216 | #'papersize': 'letterpaper', 217 | 218 | # The font size ('10pt', '11pt' or '12pt'). 219 | #'pointsize': '10pt', 220 | 221 | # Additional stuff for the LaTeX preamble. 222 | #'preamble': '', 223 | 224 | # Latex figure (float) alignment 225 | #'figure_align': 'htbp', 226 | } 227 | 228 | # Grouping the document tree into LaTeX files. List of tuples 229 | # (source start file, target name, title, 230 | # author, documentclass [howto, manual, or own class]). 231 | latex_documents = [ 232 | (master_doc, 'warpdrive.tex', u'warpdrive Documentation', 233 | u'Graham Dumpleton', 'manual'), 234 | ] 235 | 236 | # The name of an image file (relative to this directory) to place at the top of 237 | # the title page. 238 | #latex_logo = None 239 | 240 | # For "manual" documents, if this is true, then toplevel headings are parts, 241 | # not chapters. 242 | #latex_use_parts = False 243 | 244 | # If true, show page references after internal links. 245 | #latex_show_pagerefs = False 246 | 247 | # If true, show URL addresses after external links. 248 | #latex_show_urls = False 249 | 250 | # Documents to append as an appendix to all manuals. 251 | #latex_appendices = [] 252 | 253 | # If false, no module index is generated. 254 | #latex_domain_indices = True 255 | 256 | 257 | # -- Options for manual page output --------------------------------------- 258 | 259 | # One entry per manual page. List of tuples 260 | # (source start file, name, description, authors, manual section). 261 | man_pages = [ 262 | (master_doc, 'warpdrive', u'warpdrive Documentation', 263 | [author], 1) 264 | ] 265 | 266 | # If true, show URL addresses after external links. 267 | #man_show_urls = False 268 | 269 | 270 | # -- Options for Texinfo output ------------------------------------------- 271 | 272 | # Grouping the document tree into Texinfo files. List of tuples 273 | # (source start file, target name, title, author, 274 | # dir menu entry, description, category) 275 | texinfo_documents = [ 276 | (master_doc, 'warpdrive', u'warpdrive Documentation', 277 | author, 'warpdrive', 'One line description of project.', 278 | 'Miscellaneous'), 279 | ] 280 | 281 | # Documents to append as an appendix to all manuals. 282 | #texinfo_appendices = [] 283 | 284 | # If false, no module index is generated. 285 | #texinfo_domain_indices = True 286 | 287 | # How to display URL addresses: 'footnote', 'no', or 'inline'. 288 | #texinfo_show_urls = 'footnote' 289 | 290 | # If true, do not generate a @detailmenu in the "Top" node's menu. 291 | #texinfo_no_detailmenu = False 292 | -------------------------------------------------------------------------------- /docs/getting-started.rst: -------------------------------------------------------------------------------- 1 | =============== 2 | Getting Started 3 | =============== 4 | 5 | To illustrate what ``warpdrive`` is all about a simple demonstration is 6 | in order. For that we are going to first create a new Django web 7 | application project and get it running. 8 | 9 | Creating a new Django project 10 | ----------------------------- 11 | 12 | To create a new Django project a number of steps are required. The first 13 | of these is to create the Django project itself. 14 | 15 | :: 16 | 17 | $ django-admin startproject mydjangosite 18 | 19 | The ``startproject`` admin command creates the new project in a fresh 20 | subdirectory. For the next steps we need to be in that sub directory. 21 | 22 | :: 23 | 24 | $ cd mydjangosite/ 25 | 26 | Because Django sites will usually be backed by a database for persistent 27 | storage, we next need to initialise the database. For the default project 28 | the database is an instance of SQLite stored directly in the local file 29 | system. 30 | 31 | :: 32 | 33 | $ python manage.py migrate 34 | Operations to perform: 35 | Apply all migrations: admin, contenttypes, auth, sessions 36 | Running migrations: 37 | Rendering model states... DONE 38 | Applying contenttypes.0001_initial... OK 39 | Applying auth.0001_initial... OK 40 | Applying admin.0001_initial... OK 41 | Applying admin.0002_logentry_remove_auto_add... OK 42 | Applying contenttypes.0002_remove_content_type_name... OK 43 | Applying auth.0002_alter_permission_name_max_length... OK 44 | Applying auth.0003_alter_user_email_max_length... OK 45 | Applying auth.0004_alter_user_username_opts... OK 46 | Applying auth.0005_alter_user_last_login_null... OK 47 | Applying auth.0006_require_contenttypes_0002... OK 48 | Applying auth.0007_alter_validators_add_error_messages... OK 49 | Applying sessions.0001_initial... OK 50 | 51 | Once the database has been initialised, we need to create a super user 52 | account so that we will be able to login using the Django admin interface. 53 | 54 | :: 55 | 56 | $ python manage.py createsuperuser 57 | Username (leave blank to use 'grumpy'): 58 | Email address: grumpy@example.com 59 | Password: 60 | Password (again): 61 | Superuser created successfully. 62 | 63 | Finally, to start up the web site we can run the Django development server. 64 | 65 | :: 66 | 67 | $ python manage.py runserver 68 | Performing system checks... 69 | 70 | System check identified no issues (0 silenced). 71 | April 08, 2016 - 04:08:52 72 | Django version 1.9.5, using settings 'mydjangosite.settings' 73 | Starting development server at http://127.0.0.1:8000/ 74 | Quit the server with CONTROL-C. 75 | 76 | Visting the root of the the site you will be presented with the Django 77 | 'It Worked!' page. 78 | 79 | To get access to the Django admin interface, go to the URL 80 | ``http://127.0.0.1:8000/admin``. Here you will be presented with the login 81 | page for the Django admin interface. 82 | 83 | As we are using the builtin Django development server the styling for the 84 | login page should look correct as the development server automatically 85 | worries about static file assets such as style sheets. 86 | 87 | As you all hopefully know, the Django development server should not though 88 | be used for a production system. Switching from the Django development 89 | server to a production grade server and real world deployment is where 90 | things can very quickly get a lot more complicated. 91 | 92 | Not only do you have to worry about how to set up the particular Python 93 | WSGI server you choose to use, you also have to make configuration changes 94 | to your Django project to enable you to collect together any static file 95 | assets for a web server to host. For a WSGI server which doesn't provide a 96 | builtin way of hosting static files, you would also need to add in an 97 | appropriate WSGI middleware to your Django application to handle serving up 98 | of the static files. 99 | 100 | To explain all these extra steps and how to set up a particular WSGI server 101 | is out of scope for this discussion, but then it isn't important anyway. 102 | This is because ``warpdrive`` will handle all that for you. 103 | 104 | Packages and virtual environments 105 | --------------------------------- 106 | 107 | In the prior instructions, one detail which was not covered was the step of 108 | actually installing Django. It was assumed that you had already installed 109 | it. 110 | 111 | When working on a complex project, Django will not be the only Python 112 | package you will need to install. Installing every package separately is a 113 | laborious process. The more typical way installation of required packages 114 | is handled is by listing them in a ``requirements.txt`` file. This is an 115 | input file that can be supplied to ``pip``, with ``pip`` installing all 116 | the packages in one go. 117 | 118 | We therefore create the ``requirements.txt`` file and into it add: 119 | 120 | :: 121 | 122 | Django 123 | 124 | As your project grows this is where you would add new packages. Each time 125 | you add new packages you would need to rerun ``pip`` against the file to 126 | update your Python installation. 127 | 128 | Equally as important as being able to easily install packages is where you 129 | install them. As a general rule, it is a bad idea to install packages into 130 | you actual Python installation. Best practice is to always use a Python 131 | virtual environment. 132 | 133 | Using a distinct Python virtual environment for your application ensures 134 | you are able to install the actual versions of packages you need for that 135 | application. You do not have a problem of conflicting requirements arising 136 | from multiple applications sharing the same Python installation. 137 | 138 | On top of using ``pip`` you also need to be familiar with tools such as 139 | ``virtualenv`` or ``pyvenv``. 140 | 141 | Working in a local environment 142 | ------------------------------ 143 | 144 | As you can already see, there are lots of little steps you need to start 145 | remembering and would have to replicate when it comes to deploying your 146 | web application to a production environment. 147 | 148 | This is where ``warpdrive`` can help out, as it knows how to run many of 149 | these tasks for you, or can at least capture the knowledge of what needs to 150 | be run, and provides you with a simple interface to run them all at the 151 | appropriate times. 152 | 153 | Lets now start over and see how you would use ``warpdrive`` to work on 154 | the same project. 155 | 156 | The first step is to get ``warpdrive`` installed. To install ``warpdrive``, 157 | use ``pip``. 158 | 159 | :: 160 | 161 | pip install warpdrive 162 | 163 | This can be into your main Python installation, or you can create a 164 | dedicated Python virtual environment which contains only ``warpdrive``. 165 | 166 | Next we are going to add some extra lines to your login shell profile. If 167 | using ``bash``, run: 168 | 169 | :: 170 | 171 | warpdrive profile 172 | 173 | and take the output and add it to the end of your ``~/.bash_profile`` file. 174 | It should look something like: 175 | 176 | :: 177 | 178 | WARPDRIVE=$HOME/Python/warpdrive/bin/warpdrive 179 | export WARPDRIVE 180 | 181 | . `$WARPDRIVE rcfile` 182 | 183 | The ``WARPDRIVE`` variable should be set to where the ``warpdrive`` command 184 | was installed. In the case of using a Python virtual environment, there is 185 | no need to activate the Python virtual environment you installed 186 | ``warpdrive`` into. What you are adding to the login shell profile will 187 | ensure that ``warpdrive`` always works without you needing to do that. 188 | 189 | That completes the once off initial installation of ``warpdrive``. Create 190 | a new shell so the updated login shell profile is picked up and you are 191 | good to go. 192 | 193 | To start out with ``warpdrive`` we first need to set up the project for 194 | your application. While in the Django project directory, now run: 195 | 196 | :: 197 | 198 | warpdrive project --create mydjangosite 199 | 200 | The ``--create`` option is only needed the first time when the project has 201 | not already been setup. 202 | 203 | This should update your command prompt to include 204 | ``(warpdrive+mydjangosite)``. This is done so you know what environment 205 | you are working in. 206 | 207 | What this command does is create a Python virtual environment for you, 208 | specifically dedicated to the named application. It will also set a number 209 | of environment variables which will allow you to run further operations on 210 | your project no matter what directory you are in. 211 | 212 | At this point nothing has been installed that your project requires so you 213 | aren't yet ready to work on it. To get the project ready to work on and 214 | run, all you need to do though is run ``warpdrive build``. 215 | 216 | :: 217 | 218 | (warpdrive+mydjangosite) $ warpdrive build 219 | -----> Installing dependencies with pip (requirements.txt) 220 | Collecting Django (from -r requirements.txt (line 1)) 221 | Downloading Django-1.9.5-py2.py3-none-any.whl (6.6MB) 222 | 100% |████████████████████████████████| 6.6MB 968kB/s 223 | Installing collected packages: Django 224 | Successfully installed Django-1.9.5 225 | Collecting mod-wsgi 226 | Downloading mod_wsgi-4.5.1.tar.gz (1.8MB) 227 | 100% |████████████████████████████████| 1.8MB 1.2MB/s 228 | Installing collected packages: mod-wsgi 229 | Running setup.py install for mod-wsgi ... done 230 | Successfully installed mod-wsgi-4.5.1 231 | -----> Collecting static files for Django 232 | Copying '...' 233 | ... 234 | 235 | 56 static files copied to '.../warpdrive+mydjangosite/tmp/django/static'. 236 | 237 | This will install all the Python packages listed in the 238 | ``requirements.txt`` file, as well as automatically run any special steps 239 | required by Django to get an application ready to use, such as running the 240 | Django admin command ``collectstatic``. 241 | 242 | It should be pointed out that although ``warpdrive`` is running special 243 | steps here related to Django, it isn't Django specific. It will 244 | automatically detect when certain web frameworks are being used and as 245 | necessary run any special steps they require. If your web framework isn't 246 | supported, or you have your own custom steps, then ``warpdrive`` can be 247 | told about them and trigger them as part of the build as well. 248 | 249 | One example of where you will want to capture such commands is those 250 | special steps we ran to initialise the database and create the initial 251 | super user. You could run these explicitly again, but it is better to 252 | capture them in a script and add it to your application source code. You 253 | can then have ``warpdrive`` run them for you, ensuring that the correct 254 | environment has been set up so that it will work. 255 | 256 | What we are going to therefore do is create what is called an action hook. 257 | These are executable programs which ``warpdrive`` will run when needed. For 258 | any special setup steps for the application or database we are going to 259 | add them to the file ``.warpdrive/action_hooks/setup``. 260 | 261 | :: 262 | 263 | #!/bin/bash 264 | 265 | echo " -----> Running Django database migration" 266 | 267 | python manage.py migrate 268 | 269 | if [ x"$DJANGO_ADMIN_USERNAME" != x"" ]; 270 | then 271 | echo " -----> Creating predefined Django super user" 272 | (cat - | python manage.py shell) << ! 273 | from django.contrib.auth.models import User; 274 | User.objects.create_superuser('$DJANGO_ADMIN_USERNAME', 275 | '$DJANGO_ADMIN_EMAIL', 276 | '$DJANGO_ADMIN_PASSWORD') 277 | ! 278 | else 279 | if (tty > /dev/null 2>&1); then 280 | echo " -----> Running Django super user creation" 281 | python manage.py createsuperuser 282 | fi 283 | fi 284 | 285 | Having created the script and made it executable, we now run the command 286 | ``warpdrive setup``. 287 | 288 | :: 289 | 290 | (warpdrive+mydjangosite) $ warpdrive setup 291 | -----> Running Django database migration 292 | Operations to perform: 293 | Apply all migrations: admin, contenttypes, auth, sessions 294 | Running migrations: 295 | Rendering model states... DONE 296 | Applying contenttypes.0001_initial... OK 297 | Applying auth.0001_initial... OK 298 | Applying admin.0001_initial... OK 299 | Applying admin.0002_logentry_remove_auto_add... OK 300 | Applying contenttypes.0002_remove_content_type_name... OK 301 | Applying auth.0002_alter_permission_name_max_length... OK 302 | Applying auth.0003_alter_user_email_max_length... OK 303 | Applying auth.0004_alter_user_username_opts... OK 304 | Applying auth.0005_alter_user_last_login_null... OK 305 | Applying auth.0006_require_contenttypes_0002... OK 306 | Applying auth.0007_alter_validators_add_error_messages... OK 307 | Applying sessions.0001_initial... OK 308 | -----> Running Django super user creation 309 | Username (leave blank to use 'grumpy'): 310 | Email address: grumpy@example.com 311 | Password: 312 | Password (again): 313 | Superuser created successfully. 314 | 315 | We can now startup the Django web application using ``warpdrive start``. 316 | 317 | :: 318 | 319 | (warpdrive+mydjangosite) $ warpdrive start 320 | -----> Configuring for server type of 'auto' 321 | -----> Default server for 'auto' is 'mod_wsgi' 322 | -----> Running server script start-mod_wsgi 323 | -----> Executing server command 'mod_wsgi-express start-server 324 | --log-to-terminal --startup-log --port 8080 --application-type module 325 | --entry-point mydjangosite.wsgi --callable-object application 326 | --url-alias /static/ .../warpdrive+mydjangosite/tmp/django/static/' 327 | Server URL : http://localhost:8080/ 328 | Server Root : /tmp/mod_wsgi-localhost:8080:502 329 | Server Conf : /tmp/mod_wsgi-localhost:8080:502/httpd.conf 330 | Error Log File : /dev/stderr (warn) 331 | Startup Log File : /dev/stderr 332 | Request Capacity : 5 (1 process * 5 threads) 333 | Request Timeout : 60 (seconds) 334 | Queue Backlog : 100 (connections) 335 | Queue Timeout : 45 (seconds) 336 | Server Capacity : 20 (event/worker), 20 (prefork) 337 | Server Backlog : 500 (connections) 338 | Locale Setting : en_AU.UTF-8 339 | [Fri Apr 08 17:52:52.946943 2016] [mpm_prefork:notice] [pid 25978] 340 | AH00163: Apache/2.4.18 (Unix) mod_wsgi/4.5.1 Python/2.7.10 341 | configured -- resuming normal operations 342 | [Fri Apr 08 17:52:52.947336 2016] [core:notice] [pid 25978] 343 | AH00094: Command line: 'httpd (mod_wsgi-express) 344 | -f /tmp/mod_wsgi-localhost:8080:502/httpd.conf 345 | -E /dev/stderr -D FOREGROUND' 346 | 347 | We again have the Django web application running, but this time the Django 348 | development server is not being used. Instead ``mod_wsgi-express`` is being 349 | used. You will note though that you did not have to do anything to configure 350 | Apache or mod_wsgi. This is because ``warpdrive`` and ``mod_wsgi-express`` 351 | together have done that for you. 352 | 353 | Because ``mod_wsgi-express`` is being used, the same setup can be used for 354 | a production deployment as well. 355 | 356 | Even though it is a production grade WSGI server, it is still quite 357 | suitable for use in a development environment, and using the same WSGI 358 | server in both development and production would actually be preferred. This 359 | is because being the same WSGI server you are now more likely to uncover 360 | problems in development, rather than only uncovering them when you switch 361 | WSGI servers and move to production. 362 | 363 | As to features like automatic source code reloading which the Django 364 | development server offerred, this can easily be enabled using an 365 | environment, variable. 366 | 367 | :: 368 | 369 | (warpdrive+mydjangosite) $ MOD_WSGI_RELOAD_ON_CHANGES=1 warpdrive start 370 | -----> Configuring for server type of 'auto' 371 | -----> Default server for 'auto' is 'mod_wsgi' 372 | -----> Running server script start-mod_wsgi 373 | -----> Executing server command 'mod_wsgi-express start-server 374 | --log-to-terminal --startup-log --port 8080 --reload-on-changes 375 | --application-type module --entry-point mydjangosite.wsgi 376 | --callable-object application 377 | --url-alias /static/ .../warpdrive+mydjangosite/tmp/django/static/' 378 | Server URL : http://localhost:8080/ 379 | Server Root : /tmp/mod_wsgi-localhost:8080:502 380 | Server Conf : /tmp/mod_wsgi-localhost:8080:502/httpd.conf 381 | Error Log File : /dev/stderr (warn) 382 | Startup Log File : /dev/stderr 383 | Request Capacity : 5 (1 process * 5 threads) 384 | Request Timeout : 60 (seconds) 385 | Queue Backlog : 100 (connections) 386 | Queue Timeout : 45 (seconds) 387 | Server Capacity : 20 (event/worker), 20 (prefork) 388 | Server Backlog : 500 (connections) 389 | Locale Setting : en_AU.UTF-8 390 | [Fri Apr 08 21:11:18.275624 2016] [mpm_prefork:notice] [pid 26330] 391 | AH00163: Apache/2.4.18 (Unix) mod_wsgi/4.5.1 Python/2.7.10 392 | configured -- resuming normal operations 393 | [Fri Apr 08 21:11:18.275898 2016] [core:notice] [pid 26330] 394 | AH00094: Command line: 'httpd (mod_wsgi-express) 395 | -f /tmp/mod_wsgi-localhost:8080:502/httpd.conf 396 | -E /dev/stderr -D FOREGROUND' 397 | [Fri Apr 08 11:11:18.533299 2016] [wsgi:error] [pid 26332] monitor 398 | (pid=26332): Starting change monitor. 399 | 400 | In addition to automatic source code reloading, ``mod_wsgi-express`` offers 401 | a range of other development focused features builtin which can be enabled. 402 | These include interactive post mortem debugging, request auditing, 403 | profiling and code coverage. 404 | 405 | Although ``mod_wsgi-express`` offers the most flexibility as far as how it 406 | can be configured and was to a degree purpose built for this way of being 407 | used, you can if need be flag that you instead want to use other WSGI 408 | servers such as ``gunicorn``, ``uWSGI`` and ``Waitress``. In all cases when 409 | in automatic mode, as above, all the details of how to start up the WSGI 410 | server are handled for you. This includes using whatever means is 411 | appropriate for handling serving up of static files, without you needing to 412 | make changes in your application code to support that through special WSGI 413 | middleware. 414 | 415 | For example, if it had been defined that ``Waitress`` should instead be 416 | used the result would be as follows. 417 | 418 | :: 419 | 420 | (warpdrive+mydjangosite) $ warpdrive start 421 | -----> Configuring for server type of 'auto' 422 | -----> Default server for 'auto' is 'waitress' 423 | -----> Running server script start-waitress 424 | -----> Executing server command 'waitress-serve --port 8080 --threads=5 425 | whitenoise_wrapper:application' 426 | serving on http://0.0.0.0:8080 427 | 428 | Don't like how ``warpdrive`` automatically works out what options need to 429 | be supplied to the WSGI server, well you can disable that as well. In that 430 | case ``warpdrive`` will still start the WSGI server, but will only supply 431 | the absolute minimum options to have the WSGI server listen on the correct 432 | port and log output appropriately. You would then supply the specific 433 | options you want to use in a configuration file. 434 | 435 | What now if you make changes to the source code for your application? 436 | 437 | If they are just changing how the code works, but are not touching static 438 | files or database models, you can simply run ``warpdrive start`` again 439 | after making the changes. 440 | 441 | If you had made changes to the list of Python packages which needed to be 442 | installed, or if you made changes to any static files, or added new static 443 | files, then you would first run ``warpdrive build`` again. This will ensure 444 | everything is all made up to date again in preparation for running 445 | ``warpdrive start`` again. 446 | 447 | If you make changes to database models that would necessitate a database 448 | migration. As with setup of the application and database, we want to 449 | capture what is required for this as well so we don't have to remember 450 | every time. For any special steps related to database migration or 451 | otherwise migrating from one version of your application code to another, 452 | we are going to use the ``.warpdrive/action_hooks/migrate`` file. 453 | 454 | :: 455 | 456 | #!/bin/bash 457 | 458 | echo " -----> Running Django database migration" 459 | 460 | python manage.py migrate 461 | 462 | We can then run ``warpdrive migrate`` and not need to worry about the 463 | details of what needs to be run, or ensuring that the environment is setup 464 | correctly to run it as ``warpdrive`` will worry about it. 465 | 466 | :: 467 | 468 | $ warpdrive migrate 469 | -----> Running Django database migration 470 | Operations to perform: 471 | Apply all migrations: admin, contenttypes, auth, sessions 472 | Running migrations: 473 | No migrations to apply. 474 | 475 | Along with actions hooks for ``setup`` and ``migrate``, there are also 476 | others for ``build`` and ``deploy`` steps. These allow you to specify 477 | extra steps to be run when ``warpdrive build`` and ``warpdrive start`` 478 | are being run. 479 | 480 | The ``warpdrive`` command therefore provides a higher level command which 481 | can be used to hide all the steps and ensure that they are reliably 482 | performed every time they are required and in the required order. 483 | 484 | If not using Django but some other Python web framework, or even a simple 485 | WSGI ``hello world`` application, then how you use ``warpdrive`` is exactly 486 | the same. You therefore have only one set of commands to remember if using 487 | different Python web frameworks at different times as the special steps 488 | will be captured by the action hooks. 489 | 490 | Building an image for Docker 491 | ---------------------------- 492 | 493 | Once you are finished developing your web application you will next be 494 | thinking about how to deploy it. The flavour of the month for that right 495 | now is to use Docker to bundle up your Python web application and then host 496 | that Docker image in some way. 497 | 498 | Creating Docker images on the face of it appears simple, but there are in 499 | fact quite a lot of pitfulls and things you can get wrong. It is very easy 500 | to stuff things up and you can be left with an insecure environment, 501 | something that doesn't perform very well or which isn't very configurable 502 | or easy to maintain. 503 | 504 | Like how ``warpdrive`` can manage the tasks of building everything required 505 | for your web application and starting any WSGI server in the most 506 | appropriate way, it can also manage creating a Docker image for you. By 507 | using ``warpdrive`` to do this you do not need to worry at all about how to 508 | write a ``Dockerfile``. Instead ``warpdrive`` does it, employing all the 509 | best practices which may exist and providing you with a secure environment. 510 | 511 | To create a Docker image, all you need to do is run ``warpdrive image``. 512 | 513 | :: 514 | 515 | (warpdrive+mydjangosite) $ warpdrive image mydjangosite 516 | I0408 21:49:39.696772 27066 install.go:236] Using "assemble" installed from "image:///usr/local/s2i/bin/assemble" 517 | I0408 21:49:39.696877 27066 install.go:236] Using "run" installed from "image:///usr/local/s2i/bin/run" 518 | I0408 21:49:39.696900 27066 install.go:236] Using "save-artifacts" installed from "image:///usr/local/s2i/bin/save-artifacts" 519 | ---> Installing application source 520 | ---> Building application from source 521 | -----> Installing dependencies with pip (requirements.txt) 522 | Collecting Django (from -r requirements.txt (line 1)) 523 | Downloading Django-1.9.5-py2.py3-none-any.whl (6.6MB) 524 | Installing collected packages: Django 525 | Successfully installed Django-1.9.5 526 | -----> Collecting static files for Django 527 | Copying '...' 528 | 529 | 56 static files copied to '/opt/app-root/tmp/django/static'. 530 | ---> Fix permissions on application source 531 | 532 | This obviously requires you to have Docker installed locally, plus you will 533 | also need a program installed called Source to Image (S2I_). 534 | 535 | .. _S2I: https://github.com/openshift/source-to-image 536 | 537 | The S2I program which does all the hard work, uses a special Docker base 538 | image which already incorporates ``warpdrive`` and all the required Python 539 | run time and other tools that may be needed. The contents of your 540 | application are combined with that to produce the final application image 541 | you can then use. 542 | 543 | Running the Docker image is then as simple as running ``docker run``. 544 | 545 | :: 546 | 547 | (warpdrive+mydjangosite) $ docker run --rm -p 8080:8080 mydjangosite 548 | ---> Executing the start up script 549 | -----> Configuring for server type of 'auto' 550 | -----> Default server for 'auto' is 'mod_wsgi' 551 | -----> Running server script start-mod_wsgi 552 | -----> Executing server command 'mod_wsgi-express start-server 553 | --log-to-terminal --startup-log --port 8080 --application-type module 554 | --entry-point mydjangosite.wsgi --callable-object application 555 | --url-alias /static/ /tmp/django/static/' 556 | [Fri Apr 08 11:57:10.122619 2016] [mpm_event:notice] 557 | [pid 28:tid 139770362414848] AH00489: Apache/2.4.18 (Unix) 558 | mod_wsgi/4.4.22 Python/2.7.11 configured -- resuming normal operations 559 | [Fri Apr 08 11:57:10.122789 2016] [core:notice] 560 | [pid 28:tid 139770362414848] AH00094: Command line: 'httpd 561 | (mod_wsgi-express) -f /tmp/mod_wsgi-localhost:8080:1001/httpd.conf 562 | -E /dev/stderr -D MOD_WSGI_MPM_ENABLE_EVENT_MODULE 563 | -D MOD_WSGI_MPM_EXISTS_EVENT_MODULE -D MOD_WSGI_MPM_EXISTS_WORKER_MODULE 564 | -D MOD_WSGI_MPM_EXISTS_PREFORK_MODULE -D FOREGROUND' 565 | 566 | You can see that the same steps for running your Python web application are 567 | followed as when you were working in the local environment. This is because 568 | ``warpdrive`` is being used inside of the container as well. This ensures 569 | that how things were run on your local environment are as close as possible 570 | to the final deployment. 571 | 572 | As before we still need to initialise the database and setup everything for 573 | the application. Just like before ``warpdrive setup`` is used, but this 574 | time you need to run it within an initial container. We will use here the 575 | running container we just started up. 576 | 577 | :: 578 | 579 | $ docker exec -it trusting_yonath warpdrive setup 580 | -----> Running Django database migration 581 | Operations to perform: 582 | Apply all migrations: contenttypes, admin, sessions, auth 583 | Running migrations: 584 | Rendering model states... DONE 585 | Applying contenttypes.0001_initial... OK 586 | Applying auth.0001_initial... OK 587 | Applying admin.0001_initial... OK 588 | Applying admin.0002_logentry_remove_auto_add... OK 589 | Applying contenttypes.0002_remove_content_type_name... OK 590 | Applying auth.0002_alter_permission_name_max_length... OK 591 | Applying auth.0003_alter_user_email_max_length... OK 592 | Applying auth.0004_alter_user_username_opts... OK 593 | Applying auth.0005_alter_user_last_login_null... OK 594 | Applying auth.0006_require_contenttypes_0002... OK 595 | Applying auth.0007_alter_validators_add_error_messages... OK 596 | Applying sessions.0001_initial... OK 597 | -----> Running Django super user creation 598 | Username (leave blank to use 'default'): grumpy 599 | Email address: grumpy@example.com 600 | Password: 601 | Password (again): 602 | Superuser created successfully. 603 | 604 | Similarly, when necessary, ``warpdrive migrate`` can be run within a 605 | container to perform any database migration. 606 | 607 | We were only using a local filesystem database in this case, so if you were 608 | using a database like PostgreSQL or MySQL where it was running in a separate 609 | container, you would need to link the containers when running. 610 | 611 | Hosting on a Platform as a Service 612 | ---------------------------------- 613 | 614 | There is no reason why ``warpdrive`` couldn't also be used with a Platform 615 | as a Service (PasS). The only restriction would be whether the PaaS 616 | environment allows you to hook into their own system for building and 617 | deploying your web application. They also need to provide a Python 618 | installation that has been installed correctly with a shared library for 619 | Python if wanting to use ``mod_wsgi-express``. 620 | 621 | One system which is already supported by ``warpdrive`` is OpenShift 3. 622 | This platform supports the S2I image tool which was used above to create 623 | the Docker image. It is therefore a simple matter of telling OpenShift to 624 | use the appropriate S2I builder and point it at the Git repository which 625 | contains your application source code. How to do that will be covered in 626 | more detail elsewhere. 627 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | Engage Warp Drive 2 | ================= 3 | 4 | .. toctree:: 5 | :maxdepth: 2 6 | :hidden: 7 | 8 | The ``warpdrive`` project provides scripts for implementing a build and 9 | deployment system for Python web applications. It targets local 10 | development, as well as deployment directly to a managed host, a Docker 11 | hosting service, a more comprehensive container application platform, or 12 | a platform as a service (PaaS) provider. 13 | 14 | In short, ``warpdrive`` aims to make working on and deploying Python web 15 | applications easy. It does this by managing for you the tasks of building 16 | up everything required to deploy your application, including Docker images, 17 | and then also managing the startup of your Python web application. 18 | 19 | Builtin support is provided for the most popular Python WSGI servers, with 20 | hooking mechanisms to allow you to also make use of custom Python web 21 | servers, including ASYNC based web servers or frameworks. 22 | 23 | Want to get a quick feel for what ``warpdrive`` can do for you, check out 24 | :doc:`getting-started`. 25 | 26 | .. toctree:: 27 | :maxdepth: 1 28 | :hidden: 29 | 30 | getting-started 31 | -------------------------------------------------------------------------------- /getwarped.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | if [ x"$WARPDRIVE_DEBUG" != x"" ]; then 4 | set -x 5 | env 6 | fi 7 | 8 | set -eo pipefail 9 | 10 | APP_ROOT=/opt/app-root 11 | 12 | VERSION=0 13 | 14 | while [ "$#" != "0" ]; do 15 | case "$1" in 16 | --version=*) 17 | VERSION=`echo $1 | sed -e 's/--version=//'` 18 | ;; 19 | --version) 20 | VERSION=$2 21 | shift 22 | ;; 23 | --app-root=*) 24 | APP_ROOT=`echo $1 | sed -e 's/--app-root=//'` 25 | ;; 26 | --app-root) 27 | APP_ROOT=$2 28 | shift 29 | ;; 30 | esac 31 | 32 | shift 33 | done 34 | 35 | case "$VERSION" in 36 | 0) 37 | VERSION=0.34.0 38 | ;; 39 | esac 40 | 41 | PACKAGE=https://codeload.github.com/GrahamDumpleton/warpdrive/tar.gz/$VERSION 42 | 43 | curl -SL --fail -o /tmp/warpdrive.tar.gz $PACKAGE \ 44 | && tar -xC $APP_ROOT --strip-components=1 -f /tmp/warpdrive.tar.gz \ 45 | warpdrive-$VERSION/warpdrive warpdrive-$VERSION/s2i \ 46 | && rm -f /tmp/warpdrive.tar.gz 47 | 48 | mkdir -p $APP_ROOT/bin 49 | mkdir -p $APP_ROOT/tmp 50 | mkdir -p $APP_ROOT/data 51 | 52 | cat >> $APP_ROOT/bin/warpdrive << ! 53 | #!/bin/sh 54 | exec $APP_ROOT/warpdrive/bin/warpdrive "\$@" 55 | ! 56 | 57 | chmod +x $APP_ROOT/bin/warpdrive 58 | 59 | PIP_DISABLE_PIP_VERSION_CHECK=1 60 | export PIP_DISABLE_PIP_VERSION_CHECK 61 | 62 | PIP_NO_CACHE_DIR=off 63 | export PIP_NO_CACHE_DIR 64 | 65 | virtualenv $APP_ROOT 66 | 67 | PATH="$APP_ROOT/bin:$PATH" 68 | 69 | pip install -U pip 70 | 71 | warpdrive fixup $APP_ROOT 72 | 73 | if [ -f $APP_ROOT/etc/scl_enable ]; then 74 | echo ". $APP_ROOT/warpdrive/etc/shell-init" >> $APP_ROOT/etc/scl_enable 75 | fi 76 | -------------------------------------------------------------------------------- /openshift/warpdrive-python.json: -------------------------------------------------------------------------------- 1 | { 2 | "kind": "List", 3 | "apiVersion": "v1", 4 | "metadata": {}, 5 | "items": [ 6 | { 7 | "kind": "Template", 8 | "apiVersion": "v1", 9 | "metadata": { 10 | "name": "warpdrive-python27", 11 | "annotations": { 12 | "description": "Python 2.7 by Graham Dumpleton (www.getwarped.org)", 13 | "iconClass": "icon-python", 14 | "tags": "python,python27,warpdrive" 15 | } 16 | }, 17 | "objects": [ 18 | { 19 | "kind": "ImageStream", 20 | "apiVersion": "v1", 21 | "metadata": { 22 | "name": "${APPLICATION_NAME}", 23 | "labels": { 24 | "warpdrive-app": "${APPLICATION_NAME}" 25 | } 26 | } 27 | }, 28 | { 29 | "kind": "BuildConfig", 30 | "apiVersion": "v1", 31 | "metadata": { 32 | "name": "${APPLICATION_NAME}", 33 | "labels": { 34 | "warpdrive-app": "${APPLICATION_NAME}" 35 | } 36 | }, 37 | "spec": { 38 | "triggers": [ 39 | { 40 | "type": "ConfigChange" 41 | }, 42 | { 43 | "type": "ImageChange" 44 | } 45 | ], 46 | "source": { 47 | "type": "Git", 48 | "git": { 49 | "uri": "${REPOSITORY_URL}" 50 | } 51 | }, 52 | "strategy": { 53 | "type": "Source", 54 | "sourceStrategy": { 55 | "from": { 56 | "kind": "ImageStreamTag", 57 | "namespace": "openshift", 58 | "name": "python:2.7" 59 | }, 60 | "scripts": "https://raw.githubusercontent.com/GrahamDumpleton/warpdrive/master/s2i/bootstrap", 61 | "env" : [ 62 | { 63 | "name": "WARPDRIVE_PRJ_ROOT", 64 | "value": "${WARPDRIVE_PRJ_ROOT}" 65 | }, 66 | { 67 | "name": "WARPDRIVE_DEPLOY_MODE", 68 | "value": "${WARPDRIVE_DEPLOY_MODE}" 69 | }, 70 | { 71 | "name": "WARPDRIVE_SERVER_TYPE", 72 | "value": "${WARPDRIVE_SERVER_TYPE}" 73 | }, 74 | { 75 | "name": "WARPDRIVE_SHELL_FILE", 76 | "value": "${WARPDRIVE_SHELL_FILE}" 77 | }, 78 | { 79 | "name": "WARPDRIVE_PYTHON_FILE", 80 | "value": "${WARPDRIVE_PYTHON_FILE}" 81 | }, 82 | { 83 | "name": "WARPDRIVE_WSGI_MODULE_NAME", 84 | "value": "${WARPDRIVE_WSGI_MODULE_NAME}" 85 | }, 86 | { 87 | "name": "WARPDRIVE_WSGI_CALLABLE_OBJECT", 88 | "value": "${WARPDRIVE_WSGI_CALLABLE_OBJECT}" 89 | }, 90 | { 91 | "name": "WARPDRIVE_WSGI_STATIC_URL", 92 | "value": "${WARPDRIVE_WSGI_STATIC_URL}" 93 | }, 94 | { 95 | "name": "WARPDRIVE_WSGI_STATIC_ROOT", 96 | "value": "${WARPDRIVE_WSGI_STATIC_ROOT}" 97 | }, 98 | { 99 | "name": "WARPDRIVE_VERSION", 100 | "value": "${WARPDRIVE_VERSION}" 101 | }, 102 | { 103 | "name": "WARPDRIVE_DEBUG", 104 | "value": "${WARPDRIVE_DEBUG}" 105 | } 106 | ] 107 | } 108 | }, 109 | "output": { 110 | "to": { 111 | "kind": "ImageStreamTag", 112 | "name": "${APPLICATION_NAME}:latest" 113 | } 114 | } 115 | } 116 | }, 117 | { 118 | "kind": "DeploymentConfig", 119 | "apiVersion": "v1", 120 | "metadata": { 121 | "name": "${APPLICATION_NAME}", 122 | "labels": { 123 | "warpdrive-app": "${APPLICATION_NAME}" 124 | } 125 | }, 126 | "spec": { 127 | "strategy": { 128 | "type": "Rolling" 129 | }, 130 | "triggers": [ 131 | { 132 | "type": "ConfigChange" 133 | }, 134 | { 135 | "type": "ImageChange", 136 | "imageChangeParams": { 137 | "automatic": true, 138 | "containerNames": [ 139 | "${APPLICATION_NAME}" 140 | ], 141 | "from": { 142 | "kind": "ImageStreamTag", 143 | "name": "${APPLICATION_NAME}:latest" 144 | } 145 | } 146 | } 147 | ], 148 | "replicas": 1, 149 | "selector": { 150 | "warpdrive-app": "${APPLICATION_NAME}", 151 | "deploymentconfig": "${APPLICATION_NAME}" 152 | }, 153 | "template": { 154 | "metadata": { 155 | "labels": { 156 | "warpdrive-app": "${APPLICATION_NAME}", 157 | "deploymentconfig": "${APPLICATION_NAME}" 158 | } 159 | }, 160 | "spec": { 161 | "containers": [ 162 | { 163 | "name": "${APPLICATION_NAME}", 164 | "image": "${APPLICATION_NAME}:latest", 165 | "ports": [ 166 | { 167 | "containerPort": 8080, 168 | "protocol": "TCP" 169 | } 170 | ] 171 | } 172 | ] 173 | } 174 | } 175 | } 176 | }, 177 | { 178 | "kind": "Service", 179 | "apiVersion": "v1", 180 | "metadata": { 181 | "name": "${APPLICATION_NAME}", 182 | "labels": { 183 | "warpdrive-app": "${APPLICATION_NAME}" 184 | } 185 | }, 186 | "spec": { 187 | "ports": [ 188 | { 189 | "name": "8080-tcp", 190 | "protocol": "TCP", 191 | "port": 8080, 192 | "targetPort": 8080 193 | } 194 | ], 195 | "selector": { 196 | "warpdrive-app": "${APPLICATION_NAME}", 197 | "deploymentconfig": "${APPLICATION_NAME}" 198 | } 199 | } 200 | }, 201 | { 202 | "kind": "Route", 203 | "apiVersion": "v1", 204 | "metadata": { 205 | "name": "${APPLICATION_NAME}", 206 | "labels": { 207 | "warpdrive-app": "${APPLICATION_NAME}" 208 | } 209 | }, 210 | "spec": { 211 | "host": "${ROUTE_HOSTNAME}", 212 | "to": { 213 | "kind": "Service", 214 | "name": "${APPLICATION_NAME}" 215 | }, 216 | "port": { 217 | "targetPort": "8080-tcp" 218 | } 219 | } 220 | } 221 | ], 222 | "parameters": [ 223 | { 224 | "name": "APPLICATION_NAME", 225 | "displayName": "Name", 226 | "description": "Identifies the resources created for this application.", 227 | "required": true 228 | }, 229 | { 230 | "name": "REPOSITORY_URL", 231 | "displayName": "Git Repository URL", 232 | "description": "Repository for your Python web application.", 233 | "required": true 234 | }, 235 | { 236 | "name": "ROUTE_HOSTNAME", 237 | "displayName": "Public Hostname", 238 | "description": "Public hostname for your Python web application. If not specified, a hostname is generated." 239 | }, 240 | { 241 | "name": "WARPDRIVE_PRJ_ROOT", 242 | "displayName": "Project Root", 243 | "description": "The name of the directory within your repository which contains your Python web application. If not specified, it is assumed it is located in the top directory of the repository. This setting will be ignored if specified in the application source code." 244 | }, 245 | { 246 | "name": "WARPDRIVE_DEPLOY_MODE", 247 | "displayName": "Deployment Mode", 248 | "description": "Deployment mode for 'warpdrive' builder scripts. Options are: shell, python, django, wsgi, gunicorn, mod_wsgi, uwsgi, waitress. A value of 'auto' will automatically determine which of 'shell', 'python', 'django' or 'wsgi' should be used as the deployment mode based on the application source code. This setting will be ignored if specified in the application source code.", 249 | "value": "auto" 250 | }, 251 | { 252 | "name": "WARPDRIVE_SERVER_TYPE", 253 | "displayName": "Server Type", 254 | "description": "The WSGI server to be used in 'django', or 'wsgi' deployment modes. Options are: gunicorn, mod_wsgi, uwsgi, waitress. This setting will be ignored if specified in the application source code.", 255 | "value": "mod_wsgi" 256 | }, 257 | { 258 | "name": "WARPDRIVE_SHELL_FILE", 259 | "displayName": "Shell Script", 260 | "description": "The relative path name of the shell script used to launch the application. Required for deployment modes: shell.", 261 | "value": "app.sh" 262 | }, 263 | { 264 | "name": "WARPDRIVE_PYTHON_FILE", 265 | "displayName": "Python Script", 266 | "description": "The relative path name of the Python script used to launch the application. Required for deployment modes: python.", 267 | "value": "app.py" 268 | }, 269 | { 270 | "name": "WARPDRIVE_WSGI_MODULE_NAME", 271 | "displayName": "Application Module", 272 | "description": "The name of the Python module containing the WSGI application. Required for deployment modes: wsgi, gunicorn, mod_wsgi, uwsgi, waitress.", 273 | "value": "wsgi" 274 | }, 275 | { 276 | "name": "WARPDRIVE_WSGI_CALLABLE_OBJECT", 277 | "displayName": "Application Callable", 278 | "description": "The name of the callable WSGI application entry point within the application module. Required for deployment modes: wsgi, gunicorn, mod_wsgi, uwsgi, waitress.", 279 | "value": "application" 280 | }, 281 | { 282 | "name": "WARPDRIVE_WSGI_STATIC_URL", 283 | "displayName": "Static URL", 284 | "description": "The sub URL at which any static file resources should be mounted. Used in deployment modes: wsgi, gunicorn, mod_wsgi, uwsgi, waitress.", 285 | "value": "" 286 | }, 287 | { 288 | "name": "WARPDRIVE_WSGI_STATIC_ROOT", 289 | "displayName": "Static Root", 290 | "description": "The relative path name to where any static file resources are located. Used in deployment modes: wsgi, gunicorn, mod_wsgi, uwsgi, waitress.", 291 | "value": "" 292 | }, 293 | { 294 | "name": "WARPDRIVE_VERSION", 295 | "displayName": "Builder Version", 296 | "description": "Version of 'warpdrive' builder scripts to use. Defaults to latest official stable release." 297 | }, 298 | { 299 | "name": "WARPDRIVE_DEBUG", 300 | "displayName": "Script Debugging", 301 | "description": "Enable debug logging for 'warpdrive' builder scripts. Defaults to being disabled." 302 | } 303 | ], 304 | "labels": { 305 | "template": "warpdrive-python27-template" 306 | } 307 | }, 308 | { 309 | "kind": "Template", 310 | "apiVersion": "v1", 311 | "metadata": { 312 | "name": "warpdrive-python33", 313 | "annotations": { 314 | "description": "Python 3.3 by Graham Dumpleton (www.getwarped.org)", 315 | "iconClass": "icon-python", 316 | "tags": "python,python33,warpdrive" 317 | } 318 | }, 319 | "objects": [ 320 | { 321 | "kind": "ImageStream", 322 | "apiVersion": "v1", 323 | "metadata": { 324 | "name": "${APPLICATION_NAME}", 325 | "labels": { 326 | "warpdrive-app": "${APPLICATION_NAME}" 327 | } 328 | } 329 | }, 330 | { 331 | "kind": "BuildConfig", 332 | "apiVersion": "v1", 333 | "metadata": { 334 | "name": "${APPLICATION_NAME}", 335 | "labels": { 336 | "warpdrive-app": "${APPLICATION_NAME}" 337 | } 338 | }, 339 | "spec": { 340 | "triggers": [ 341 | { 342 | "type": "ConfigChange" 343 | }, 344 | { 345 | "type": "ImageChange" 346 | } 347 | ], 348 | "source": { 349 | "type": "Git", 350 | "git": { 351 | "uri": "${REPOSITORY_URL}" 352 | } 353 | }, 354 | "strategy": { 355 | "type": "Source", 356 | "sourceStrategy": { 357 | "from": { 358 | "kind": "ImageStreamTag", 359 | "namespace": "openshift", 360 | "name": "python:3.3" 361 | }, 362 | "scripts": "https://raw.githubusercontent.com/GrahamDumpleton/warpdrive/master/s2i/bootstrap", 363 | "env" : [ 364 | { 365 | "name": "WARPDRIVE_PRJ_ROOT", 366 | "value": "${WARPDRIVE_PRJ_ROOT}" 367 | }, 368 | { 369 | "name": "WARPDRIVE_DEPLOY_MODE", 370 | "value": "${WARPDRIVE_DEPLOY_MODE}" 371 | }, 372 | { 373 | "name": "WARPDRIVE_SERVER_TYPE", 374 | "value": "${WARPDRIVE_SERVER_TYPE}" 375 | }, 376 | { 377 | "name": "WARPDRIVE_SHELL_FILE", 378 | "value": "${WARPDRIVE_SHELL_FILE}" 379 | }, 380 | { 381 | "name": "WARPDRIVE_PYTHON_FILE", 382 | "value": "${WARPDRIVE_PYTHON_FILE}" 383 | }, 384 | { 385 | "name": "WARPDRIVE_WSGI_MODULE_NAME", 386 | "value": "${WARPDRIVE_WSGI_MODULE_NAME}" 387 | }, 388 | { 389 | "name": "WARPDRIVE_WSGI_CALLABLE_OBJECT", 390 | "value": "${WARPDRIVE_WSGI_CALLABLE_OBJECT}" 391 | }, 392 | { 393 | "name": "WARPDRIVE_WSGI_STATIC_URL", 394 | "value": "${WARPDRIVE_WSGI_STATIC_URL}" 395 | }, 396 | { 397 | "name": "WARPDRIVE_WSGI_STATIC_ROOT", 398 | "value": "${WARPDRIVE_WSGI_STATIC_ROOT}" 399 | }, 400 | { 401 | "name": "WARPDRIVE_VERSION", 402 | "value": "${WARPDRIVE_VERSION}" 403 | }, 404 | { 405 | "name": "WARPDRIVE_DEBUG", 406 | "value": "${WARPDRIVE_DEBUG}" 407 | } 408 | ] 409 | } 410 | }, 411 | "output": { 412 | "to": { 413 | "kind": "ImageStreamTag", 414 | "name": "${APPLICATION_NAME}:latest" 415 | } 416 | } 417 | } 418 | }, 419 | { 420 | "kind": "DeploymentConfig", 421 | "apiVersion": "v1", 422 | "metadata": { 423 | "name": "${APPLICATION_NAME}", 424 | "labels": { 425 | "warpdrive-app": "${APPLICATION_NAME}" 426 | } 427 | }, 428 | "spec": { 429 | "strategy": { 430 | "type": "Rolling" 431 | }, 432 | "triggers": [ 433 | { 434 | "type": "ConfigChange" 435 | }, 436 | { 437 | "type": "ImageChange", 438 | "imageChangeParams": { 439 | "automatic": true, 440 | "containerNames": [ 441 | "${APPLICATION_NAME}" 442 | ], 443 | "from": { 444 | "kind": "ImageStreamTag", 445 | "name": "${APPLICATION_NAME}:latest" 446 | } 447 | } 448 | } 449 | ], 450 | "replicas": 1, 451 | "selector": { 452 | "warpdrive-app": "${APPLICATION_NAME}", 453 | "deploymentconfig": "${APPLICATION_NAME}" 454 | }, 455 | "template": { 456 | "metadata": { 457 | "labels": { 458 | "warpdrive-app": "${APPLICATION_NAME}", 459 | "deploymentconfig": "${APPLICATION_NAME}" 460 | } 461 | }, 462 | "spec": { 463 | "containers": [ 464 | { 465 | "name": "${APPLICATION_NAME}", 466 | "image": "${APPLICATION_NAME}:latest", 467 | "ports": [ 468 | { 469 | "containerPort": 8080, 470 | "protocol": "TCP" 471 | } 472 | ] 473 | } 474 | ] 475 | } 476 | } 477 | } 478 | }, 479 | { 480 | "kind": "Service", 481 | "apiVersion": "v1", 482 | "metadata": { 483 | "name": "${APPLICATION_NAME}", 484 | "labels": { 485 | "warpdrive-app": "${APPLICATION_NAME}" 486 | } 487 | }, 488 | "spec": { 489 | "ports": [ 490 | { 491 | "name": "8080-tcp", 492 | "protocol": "TCP", 493 | "port": 8080, 494 | "targetPort": 8080 495 | } 496 | ], 497 | "selector": { 498 | "warpdrive-app": "${APPLICATION_NAME}", 499 | "deploymentconfig": "${APPLICATION_NAME}" 500 | } 501 | } 502 | }, 503 | { 504 | "kind": "Route", 505 | "apiVersion": "v1", 506 | "metadata": { 507 | "name": "${APPLICATION_NAME}", 508 | "labels": { 509 | "warpdrive-app": "${APPLICATION_NAME}" 510 | } 511 | }, 512 | "spec": { 513 | "host": "${ROUTE_HOSTNAME}", 514 | "to": { 515 | "kind": "Service", 516 | "name": "${APPLICATION_NAME}" 517 | }, 518 | "port": { 519 | "targetPort": "8080-tcp" 520 | } 521 | } 522 | } 523 | ], 524 | "parameters": [ 525 | { 526 | "name": "APPLICATION_NAME", 527 | "displayName": "Name", 528 | "description": "Identifies the resources created for this application.", 529 | "required": true 530 | }, 531 | { 532 | "name": "REPOSITORY_URL", 533 | "displayName": "Git Repository URL", 534 | "description": "Repository for your Python web application.", 535 | "required": true 536 | }, 537 | { 538 | "name": "ROUTE_HOSTNAME", 539 | "displayName": "Public Hostname", 540 | "description": "Public hostname for your Python web application. If not specified, a hostname is generated." 541 | }, 542 | { 543 | "name": "WARPDRIVE_PRJ_ROOT", 544 | "displayName": "Project Root", 545 | "description": "The name of the directory within your repository which contains your Python web application. If not specified, it is assumed it is located in the top directory of the repository. This setting will be ignored if specified in the application source code." 546 | }, 547 | { 548 | "name": "WARPDRIVE_DEPLOY_MODE", 549 | "displayName": "Deployment Mode", 550 | "description": "Deployment mode for 'warpdrive' builder scripts. Options are: shell, python, django, wsgi, gunicorn, mod_wsgi, uwsgi, waitress. A value of 'auto' will automatically determine which of 'shell', 'python', 'django' or 'wsgi' should be used as the deployment mode based on the application source code. This setting will be ignored if specified in the application source code.", 551 | "value": "auto" 552 | }, 553 | { 554 | "name": "WARPDRIVE_SERVER_TYPE", 555 | "displayName": "Server Type", 556 | "description": "The WSGI server to be used in 'django', or 'wsgi' deployment modes. Options are: gunicorn, mod_wsgi, uwsgi, waitress. This setting will be ignored if specified in the application source code.", 557 | "value": "mod_wsgi" 558 | }, 559 | { 560 | "name": "WARPDRIVE_SHELL_FILE", 561 | "displayName": "Shell Script", 562 | "description": "The relative path name of the shell script used to launch the application. Required for deployment modes: shell.", 563 | "value": "app.sh" 564 | }, 565 | { 566 | "name": "WARPDRIVE_PYTHON_FILE", 567 | "displayName": "Python Script", 568 | "description": "The relative path name of the Python script used to launch the application. Required for deployment modes: python.", 569 | "value": "app.py" 570 | }, 571 | { 572 | "name": "WARPDRIVE_WSGI_MODULE_NAME", 573 | "displayName": "Application Module", 574 | "description": "The name of the Python module containing the WSGI application. Required for deployment modes: wsgi, gunicorn, mod_wsgi, uwsgi, waitress.", 575 | "value": "wsgi" 576 | }, 577 | { 578 | "name": "WARPDRIVE_WSGI_CALLABLE_OBJECT", 579 | "displayName": "Application Callable", 580 | "description": "The name of the callable WSGI application entry point within the application module. Required for deployment modes: wsgi, gunicorn, mod_wsgi, uwsgi, waitress.", 581 | "value": "application" 582 | }, 583 | { 584 | "name": "WARPDRIVE_WSGI_STATIC_URL", 585 | "displayName": "Static URL", 586 | "description": "The sub URL at which any static file resources should be mounted. Used in deployment modes: wsgi, gunicorn, mod_wsgi, uwsgi, waitress.", 587 | "value": "" 588 | }, 589 | { 590 | "name": "WARPDRIVE_WSGI_STATIC_ROOT", 591 | "displayName": "Static Root", 592 | "description": "The relative path name to where any static file resources are located. Used in deployment modes: wsgi, gunicorn, mod_wsgi, uwsgi, waitress.", 593 | "value": "" 594 | }, 595 | { 596 | "name": "WARPDRIVE_VERSION", 597 | "displayName": "Builder Version", 598 | "description": "Version of 'warpdrive' builder scripts to use. Defaults to latest official stable release." 599 | }, 600 | { 601 | "name": "WARPDRIVE_DEBUG", 602 | "displayName": "Script Debugging", 603 | "description": "Enable debug logging for 'warpdrive' builder scripts. Defaults to being disabled." 604 | } 605 | ], 606 | "labels": { 607 | "template": "warpdrive-python33-template" 608 | } 609 | }, 610 | { 611 | "kind": "Template", 612 | "apiVersion": "v1", 613 | "metadata": { 614 | "name": "warpdrive-python34", 615 | "annotations": { 616 | "description": "Python 3.4 by Graham Dumpleton (www.getwarped.org)", 617 | "iconClass": "icon-python", 618 | "tags": "python,python34,warpdrive" 619 | } 620 | }, 621 | "objects": [ 622 | { 623 | "kind": "ImageStream", 624 | "apiVersion": "v1", 625 | "metadata": { 626 | "name": "${APPLICATION_NAME}", 627 | "labels": { 628 | "warpdrive-app": "${APPLICATION_NAME}" 629 | } 630 | } 631 | }, 632 | { 633 | "kind": "BuildConfig", 634 | "apiVersion": "v1", 635 | "metadata": { 636 | "name": "${APPLICATION_NAME}", 637 | "labels": { 638 | "warpdrive-app": "${APPLICATION_NAME}" 639 | } 640 | }, 641 | "spec": { 642 | "triggers": [ 643 | { 644 | "type": "ConfigChange" 645 | }, 646 | { 647 | "type": "ImageChange" 648 | } 649 | ], 650 | "source": { 651 | "type": "Git", 652 | "git": { 653 | "uri": "${REPOSITORY_URL}" 654 | } 655 | }, 656 | "strategy": { 657 | "type": "Source", 658 | "sourceStrategy": { 659 | "from": { 660 | "kind": "ImageStreamTag", 661 | "namespace": "openshift", 662 | "name": "python:3.4" 663 | }, 664 | "scripts": "https://raw.githubusercontent.com/GrahamDumpleton/warpdrive/master/s2i/bootstrap", 665 | "env" : [ 666 | { 667 | "name": "WARPDRIVE_PRJ_ROOT", 668 | "value": "${WARPDRIVE_PRJ_ROOT}" 669 | }, 670 | { 671 | "name": "WARPDRIVE_DEPLOY_MODE", 672 | "value": "${WARPDRIVE_DEPLOY_MODE}" 673 | }, 674 | { 675 | "name": "WARPDRIVE_SERVER_TYPE", 676 | "value": "${WARPDRIVE_SERVER_TYPE}" 677 | }, 678 | { 679 | "name": "WARPDRIVE_SHELL_FILE", 680 | "value": "${WARPDRIVE_SHELL_FILE}" 681 | }, 682 | { 683 | "name": "WARPDRIVE_PYTHON_FILE", 684 | "value": "${WARPDRIVE_PYTHON_FILE}" 685 | }, 686 | { 687 | "name": "WARPDRIVE_WSGI_MODULE_NAME", 688 | "value": "${WARPDRIVE_WSGI_MODULE_NAME}" 689 | }, 690 | { 691 | "name": "WARPDRIVE_WSGI_CALLABLE_OBJECT", 692 | "value": "${WARPDRIVE_WSGI_CALLABLE_OBJECT}" 693 | }, 694 | { 695 | "name": "WARPDRIVE_WSGI_STATIC_URL", 696 | "value": "${WARPDRIVE_WSGI_STATIC_URL}" 697 | }, 698 | { 699 | "name": "WARPDRIVE_WSGI_STATIC_ROOT", 700 | "value": "${WARPDRIVE_WSGI_STATIC_ROOT}" 701 | }, 702 | { 703 | "name": "WARPDRIVE_VERSION", 704 | "value": "${WARPDRIVE_VERSION}" 705 | }, 706 | { 707 | "name": "WARPDRIVE_DEBUG", 708 | "value": "${WARPDRIVE_DEBUG}" 709 | } 710 | ] 711 | } 712 | }, 713 | "output": { 714 | "to": { 715 | "kind": "ImageStreamTag", 716 | "name": "${APPLICATION_NAME}:latest" 717 | } 718 | } 719 | } 720 | }, 721 | { 722 | "kind": "DeploymentConfig", 723 | "apiVersion": "v1", 724 | "metadata": { 725 | "name": "${APPLICATION_NAME}", 726 | "labels": { 727 | "warpdrive-app": "${APPLICATION_NAME}" 728 | } 729 | }, 730 | "spec": { 731 | "strategy": { 732 | "type": "Rolling" 733 | }, 734 | "triggers": [ 735 | { 736 | "type": "ConfigChange" 737 | }, 738 | { 739 | "type": "ImageChange", 740 | "imageChangeParams": { 741 | "automatic": true, 742 | "containerNames": [ 743 | "${APPLICATION_NAME}" 744 | ], 745 | "from": { 746 | "kind": "ImageStreamTag", 747 | "name": "${APPLICATION_NAME}:latest" 748 | } 749 | } 750 | } 751 | ], 752 | "replicas": 1, 753 | "selector": { 754 | "warpdrive-app": "${APPLICATION_NAME}", 755 | "deploymentconfig": "${APPLICATION_NAME}" 756 | }, 757 | "template": { 758 | "metadata": { 759 | "labels": { 760 | "warpdrive-app": "${APPLICATION_NAME}", 761 | "deploymentconfig": "${APPLICATION_NAME}" 762 | } 763 | }, 764 | "spec": { 765 | "containers": [ 766 | { 767 | "name": "${APPLICATION_NAME}", 768 | "image": "${APPLICATION_NAME}:latest", 769 | "ports": [ 770 | { 771 | "containerPort": 8080, 772 | "protocol": "TCP" 773 | } 774 | ] 775 | } 776 | ] 777 | } 778 | } 779 | } 780 | }, 781 | { 782 | "kind": "Service", 783 | "apiVersion": "v1", 784 | "metadata": { 785 | "name": "${APPLICATION_NAME}", 786 | "labels": { 787 | "warpdrive-app": "${APPLICATION_NAME}" 788 | } 789 | }, 790 | "spec": { 791 | "ports": [ 792 | { 793 | "name": "8080-tcp", 794 | "protocol": "TCP", 795 | "port": 8080, 796 | "targetPort": 8080 797 | } 798 | ], 799 | "selector": { 800 | "warpdrive-app": "${APPLICATION_NAME}", 801 | "deploymentconfig": "${APPLICATION_NAME}" 802 | } 803 | } 804 | }, 805 | { 806 | "kind": "Route", 807 | "apiVersion": "v1", 808 | "metadata": { 809 | "name": "${APPLICATION_NAME}", 810 | "labels": { 811 | "warpdrive-app": "${APPLICATION_NAME}" 812 | } 813 | }, 814 | "spec": { 815 | "host": "${ROUTE_HOSTNAME}", 816 | "to": { 817 | "kind": "Service", 818 | "name": "${APPLICATION_NAME}" 819 | }, 820 | "port": { 821 | "targetPort": "8080-tcp" 822 | } 823 | } 824 | } 825 | ], 826 | "parameters": [ 827 | { 828 | "name": "APPLICATION_NAME", 829 | "displayName": "Name", 830 | "description": "Identifies the resources created for this application.", 831 | "required": true 832 | }, 833 | { 834 | "name": "REPOSITORY_URL", 835 | "displayName": "Git Repository URL", 836 | "description": "Repository for your Python web application.", 837 | "required": true 838 | }, 839 | { 840 | "name": "ROUTE_HOSTNAME", 841 | "displayName": "Public Hostname", 842 | "description": "Public hostname for your Python web application. If not specified, a hostname is generated." 843 | }, 844 | { 845 | "name": "WARPDRIVE_PRJ_ROOT", 846 | "displayName": "Project Root", 847 | "description": "The name of the directory within your repository which contains your Python web application. If not specified, it is assumed it is located in the top directory of the repository. This setting will be ignored if specified in the application source code." 848 | }, 849 | { 850 | "name": "WARPDRIVE_DEPLOY_MODE", 851 | "displayName": "Deployment Mode", 852 | "description": "Deployment mode for 'warpdrive' builder scripts. Options are: shell, python, django, wsgi, gunicorn, mod_wsgi, uwsgi, waitress. A value of 'auto' will automatically determine which of 'shell', 'python', 'django' or 'wsgi' should be used as the deployment mode based on the application source code. This setting will be ignored if specified in the application source code.", 853 | "value": "auto" 854 | }, 855 | { 856 | "name": "WARPDRIVE_SERVER_TYPE", 857 | "displayName": "Server Type", 858 | "description": "The WSGI server to be used in 'django', or 'wsgi' deployment modes. Options are: gunicorn, mod_wsgi, uwsgi, waitress. This setting will be ignored if specified in the application source code.", 859 | "value": "mod_wsgi" 860 | }, 861 | { 862 | "name": "WARPDRIVE_SHELL_FILE", 863 | "displayName": "Shell Script", 864 | "description": "The relative path name of the shell script used to launch the application. Required for deployment modes: shell.", 865 | "value": "app.sh" 866 | }, 867 | { 868 | "name": "WARPDRIVE_PYTHON_FILE", 869 | "displayName": "Python Script", 870 | "description": "The relative path name of the Python script used to launch the application. Required for deployment modes: python.", 871 | "value": "app.py" 872 | }, 873 | { 874 | "name": "WARPDRIVE_WSGI_MODULE_NAME", 875 | "displayName": "Application Module", 876 | "description": "The name of the Python module containing the WSGI application. Required for deployment modes: wsgi, gunicorn, mod_wsgi, uwsgi, waitress.", 877 | "value": "wsgi" 878 | }, 879 | { 880 | "name": "WARPDRIVE_WSGI_CALLABLE_OBJECT", 881 | "displayName": "Application Callable", 882 | "description": "The name of the callable WSGI application entry point within the application module. Required for deployment modes: wsgi, gunicorn, mod_wsgi, uwsgi, waitress.", 883 | "value": "application" 884 | }, 885 | { 886 | "name": "WARPDRIVE_WSGI_STATIC_URL", 887 | "displayName": "Static URL", 888 | "description": "The sub URL at which any static file resources should be mounted. Used in deployment modes: wsgi, gunicorn, mod_wsgi, uwsgi, waitress.", 889 | "value": "" 890 | }, 891 | { 892 | "name": "WARPDRIVE_WSGI_STATIC_ROOT", 893 | "displayName": "Static Root", 894 | "description": "The relative path name to where any static file resources are located. Used in deployment modes: wsgi, gunicorn, mod_wsgi, uwsgi, waitress.", 895 | "value": "" 896 | }, 897 | { 898 | "name": "WARPDRIVE_VERSION", 899 | "displayName": "Builder Version", 900 | "description": "Version of 'warpdrive' builder scripts to use. Defaults to latest official stable release." 901 | }, 902 | { 903 | "name": "WARPDRIVE_DEBUG", 904 | "displayName": "Script Debugging", 905 | "description": "Enable debug logging for 'warpdrive' builder scripts. Defaults to being disabled." 906 | } 907 | ], 908 | "labels": { 909 | "template": "warpdrive-python34-template" 910 | } 911 | }, 912 | { 913 | "kind": "Template", 914 | "apiVersion": "v1", 915 | "metadata": { 916 | "name": "warpdrive-python35", 917 | "annotations": { 918 | "description": "Python 3.5 by Graham Dumpleton (www.getwarped.org)", 919 | "iconClass": "icon-python", 920 | "tags": "python,python35,warpdrive" 921 | } 922 | }, 923 | "objects": [ 924 | { 925 | "kind": "ImageStream", 926 | "apiVersion": "v1", 927 | "metadata": { 928 | "name": "${APPLICATION_NAME}", 929 | "labels": { 930 | "warpdrive-app": "${APPLICATION_NAME}" 931 | } 932 | } 933 | }, 934 | { 935 | "kind": "BuildConfig", 936 | "apiVersion": "v1", 937 | "metadata": { 938 | "name": "${APPLICATION_NAME}", 939 | "labels": { 940 | "warpdrive-app": "${APPLICATION_NAME}" 941 | } 942 | }, 943 | "spec": { 944 | "triggers": [ 945 | { 946 | "type": "ConfigChange" 947 | }, 948 | { 949 | "type": "ImageChange" 950 | } 951 | ], 952 | "source": { 953 | "type": "Git", 954 | "git": { 955 | "uri": "${REPOSITORY_URL}" 956 | } 957 | }, 958 | "strategy": { 959 | "type": "Source", 960 | "sourceStrategy": { 961 | "from": { 962 | "kind": "ImageStreamTag", 963 | "namespace": "openshift", 964 | "name": "python:3.5" 965 | }, 966 | "scripts": "https://raw.githubusercontent.com/GrahamDumpleton/warpdrive/master/s2i/bootstrap", 967 | "env" : [ 968 | { 969 | "name": "WARPDRIVE_PRJ_ROOT", 970 | "value": "${WARPDRIVE_PRJ_ROOT}" 971 | }, 972 | { 973 | "name": "WARPDRIVE_DEPLOY_MODE", 974 | "value": "${WARPDRIVE_DEPLOY_MODE}" 975 | }, 976 | { 977 | "name": "WARPDRIVE_SERVER_TYPE", 978 | "value": "${WARPDRIVE_SERVER_TYPE}" 979 | }, 980 | { 981 | "name": "WARPDRIVE_SHELL_FILE", 982 | "value": "${WARPDRIVE_SHELL_FILE}" 983 | }, 984 | { 985 | "name": "WARPDRIVE_PYTHON_FILE", 986 | "value": "${WARPDRIVE_PYTHON_FILE}" 987 | }, 988 | { 989 | "name": "WARPDRIVE_WSGI_MODULE_NAME", 990 | "value": "${WARPDRIVE_WSGI_MODULE_NAME}" 991 | }, 992 | { 993 | "name": "WARPDRIVE_WSGI_CALLABLE_OBJECT", 994 | "value": "${WARPDRIVE_WSGI_CALLABLE_OBJECT}" 995 | }, 996 | { 997 | "name": "WARPDRIVE_WSGI_STATIC_URL", 998 | "value": "${WARPDRIVE_WSGI_STATIC_URL}" 999 | }, 1000 | { 1001 | "name": "WARPDRIVE_WSGI_STATIC_ROOT", 1002 | "value": "${WARPDRIVE_WSGI_STATIC_ROOT}" 1003 | }, 1004 | { 1005 | "name": "WARPDRIVE_VERSION", 1006 | "value": "${WARPDRIVE_VERSION}" 1007 | }, 1008 | { 1009 | "name": "WARPDRIVE_DEBUG", 1010 | "value": "${WARPDRIVE_DEBUG}" 1011 | } 1012 | ] 1013 | } 1014 | }, 1015 | "output": { 1016 | "to": { 1017 | "kind": "ImageStreamTag", 1018 | "name": "${APPLICATION_NAME}:latest" 1019 | } 1020 | } 1021 | } 1022 | }, 1023 | { 1024 | "kind": "DeploymentConfig", 1025 | "apiVersion": "v1", 1026 | "metadata": { 1027 | "name": "${APPLICATION_NAME}", 1028 | "labels": { 1029 | "warpdrive-app": "${APPLICATION_NAME}" 1030 | } 1031 | }, 1032 | "spec": { 1033 | "strategy": { 1034 | "type": "Rolling" 1035 | }, 1036 | "triggers": [ 1037 | { 1038 | "type": "ConfigChange" 1039 | }, 1040 | { 1041 | "type": "ImageChange", 1042 | "imageChangeParams": { 1043 | "automatic": true, 1044 | "containerNames": [ 1045 | "${APPLICATION_NAME}" 1046 | ], 1047 | "from": { 1048 | "kind": "ImageStreamTag", 1049 | "name": "${APPLICATION_NAME}:latest" 1050 | } 1051 | } 1052 | } 1053 | ], 1054 | "replicas": 1, 1055 | "selector": { 1056 | "warpdrive-app": "${APPLICATION_NAME}", 1057 | "deploymentconfig": "${APPLICATION_NAME}" 1058 | }, 1059 | "template": { 1060 | "metadata": { 1061 | "labels": { 1062 | "warpdrive-app": "${APPLICATION_NAME}", 1063 | "deploymentconfig": "${APPLICATION_NAME}" 1064 | } 1065 | }, 1066 | "spec": { 1067 | "containers": [ 1068 | { 1069 | "name": "${APPLICATION_NAME}", 1070 | "image": "${APPLICATION_NAME}:latest", 1071 | "ports": [ 1072 | { 1073 | "containerPort": 8080, 1074 | "protocol": "TCP" 1075 | } 1076 | ] 1077 | } 1078 | ] 1079 | } 1080 | } 1081 | } 1082 | }, 1083 | { 1084 | "kind": "Service", 1085 | "apiVersion": "v1", 1086 | "metadata": { 1087 | "name": "${APPLICATION_NAME}", 1088 | "labels": { 1089 | "warpdrive-app": "${APPLICATION_NAME}" 1090 | } 1091 | }, 1092 | "spec": { 1093 | "ports": [ 1094 | { 1095 | "name": "8080-tcp", 1096 | "protocol": "TCP", 1097 | "port": 8080, 1098 | "targetPort": 8080 1099 | } 1100 | ], 1101 | "selector": { 1102 | "warpdrive-app": "${APPLICATION_NAME}", 1103 | "deploymentconfig": "${APPLICATION_NAME}" 1104 | } 1105 | } 1106 | }, 1107 | { 1108 | "kind": "Route", 1109 | "apiVersion": "v1", 1110 | "metadata": { 1111 | "name": "${APPLICATION_NAME}", 1112 | "labels": { 1113 | "warpdrive-app": "${APPLICATION_NAME}" 1114 | } 1115 | }, 1116 | "spec": { 1117 | "host": "${ROUTE_HOSTNAME}", 1118 | "to": { 1119 | "kind": "Service", 1120 | "name": "${APPLICATION_NAME}" 1121 | }, 1122 | "port": { 1123 | "targetPort": "8080-tcp" 1124 | } 1125 | } 1126 | } 1127 | ], 1128 | "parameters": [ 1129 | { 1130 | "name": "APPLICATION_NAME", 1131 | "displayName": "Name", 1132 | "description": "Identifies the resources created for this application.", 1133 | "required": true 1134 | }, 1135 | { 1136 | "name": "REPOSITORY_URL", 1137 | "displayName": "Git Repository URL", 1138 | "description": "Repository for your Python web application.", 1139 | "required": true 1140 | }, 1141 | { 1142 | "name": "ROUTE_HOSTNAME", 1143 | "displayName": "Public Hostname", 1144 | "description": "Public hostname for your Python web application. If not specified, a hostname is generated." 1145 | }, 1146 | { 1147 | "name": "WARPDRIVE_PRJ_ROOT", 1148 | "displayName": "Project Root", 1149 | "description": "The name of the directory within your repository which contains your Python web application. If not specified, it is assumed it is located in the top directory of the repository. This setting will be ignored if specified in the application source code." 1150 | }, 1151 | { 1152 | "name": "WARPDRIVE_DEPLOY_MODE", 1153 | "displayName": "Deployment Mode", 1154 | "description": "Deployment mode for 'warpdrive' builder scripts. Options are: shell, python, django, wsgi, gunicorn, mod_wsgi, uwsgi, waitress. A value of 'auto' will automatically determine which of 'shell', 'python', 'django' or 'wsgi' should be used as the deployment mode based on the application source code. This setting will be ignored if specified in the application source code.", 1155 | "value": "auto" 1156 | }, 1157 | { 1158 | "name": "WARPDRIVE_SERVER_TYPE", 1159 | "displayName": "Server Type", 1160 | "description": "The WSGI server to be used in 'django', or 'wsgi' deployment modes. Options are: gunicorn, mod_wsgi, uwsgi, waitress. This setting will be ignored if specified in the application source code.", 1161 | "value": "mod_wsgi" 1162 | }, 1163 | { 1164 | "name": "WARPDRIVE_SHELL_FILE", 1165 | "displayName": "Shell Script", 1166 | "description": "The relative path name of the shell script used to launch the application. Required for deployment modes: shell.", 1167 | "value": "app.sh" 1168 | }, 1169 | { 1170 | "name": "WARPDRIVE_PYTHON_FILE", 1171 | "displayName": "Python Script", 1172 | "description": "The relative path name of the Python script used to launch the application. Required for deployment modes: python.", 1173 | "value": "app.py" 1174 | }, 1175 | { 1176 | "name": "WARPDRIVE_WSGI_MODULE_NAME", 1177 | "displayName": "Application Module", 1178 | "description": "The name of the Python module containing the WSGI application. Required for deployment modes: wsgi, gunicorn, mod_wsgi, uwsgi, waitress.", 1179 | "value": "wsgi" 1180 | }, 1181 | { 1182 | "name": "WARPDRIVE_WSGI_CALLABLE_OBJECT", 1183 | "displayName": "Application Callable", 1184 | "description": "The name of the callable WSGI application entry point within the application module. Required for deployment modes: wsgi, gunicorn, mod_wsgi, uwsgi, waitress.", 1185 | "value": "application" 1186 | }, 1187 | { 1188 | "name": "WARPDRIVE_WSGI_STATIC_URL", 1189 | "displayName": "Static URL", 1190 | "description": "The sub URL at which any static file resources should be mounted. Used in deployment modes: wsgi, gunicorn, mod_wsgi, uwsgi, waitress.", 1191 | "value": "" 1192 | }, 1193 | { 1194 | "name": "WARPDRIVE_WSGI_STATIC_ROOT", 1195 | "displayName": "Static Root", 1196 | "description": "The relative path name to where any static file resources are located. Used in deployment modes: wsgi, gunicorn, mod_wsgi, uwsgi, waitress.", 1197 | "value": "" 1198 | }, 1199 | { 1200 | "name": "WARPDRIVE_VERSION", 1201 | "displayName": "Builder Version", 1202 | "description": "Version of 'warpdrive' builder scripts to use. Defaults to latest official stable release." 1203 | }, 1204 | { 1205 | "name": "WARPDRIVE_DEBUG", 1206 | "displayName": "Script Debugging", 1207 | "description": "Enable debug logging for 'warpdrive' builder scripts. Defaults to being disabled." 1208 | } 1209 | ], 1210 | "labels": { 1211 | "template": "warpdrive-python35-template" 1212 | } 1213 | } 1214 | ] 1215 | } 1216 | -------------------------------------------------------------------------------- /s2i/bin/assemble: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ x"$WARPDRIVE_DEBUG" != x"" ]; then 4 | set -x 5 | fi 6 | 7 | set -eo pipefail 8 | 9 | # Root directory for the runtime environment of the application. 10 | 11 | WARPDRIVE_APP_ROOT=/opt/app-root 12 | 13 | # Check for any 'assemble' script which may have been added into the 14 | # image by an initial S2I build with the resulting image being used as a 15 | # new S2I builder. When one exists we execute it, with it overriding 16 | # this script. 17 | 18 | if [ x"$WARPDRIVE_S2I_PHASE" = x"" ]; then 19 | WARPDRIVE_S2I_PHASE=assemble 20 | export WARPDRIVE_S2I_PHASE 21 | 22 | if [ -x $WARPDRIVE_APP_ROOT/.s2i/bin/assemble ]; then 23 | exec $WARPDRIVE_APP_ROOT/.s2i/bin/assemble 24 | fi 25 | fi 26 | 27 | # Root directory for the source code of the application. 28 | 29 | WARPDRIVE_SRC_ROOT=$WARPDRIVE_APP_ROOT/src 30 | 31 | # Copy the application source files into place and then remove the 32 | # original files. We can't just move the files as the target directory 33 | # already exists and we need to ensure that file system ownership 34 | # permissions as may be dictated by the target directory are inherited. 35 | 36 | if [ -d /tmp/artifacts/wheelhouse ]; then 37 | echo "---> Restoring wheelhouse from prior build" 38 | 39 | mkdir -p $WARPDRIVE_SRC_ROOT/.warpdrive/wheelhouse 40 | 41 | cp -R /tmp/artifacts/wheelhouse/* $WARPDRIVE_SRC_ROOT/.warpdrive/wheelhouse 42 | 43 | mkdir -p $WARPDRIVE_SRC_ROOT/.warpdrive/packages 44 | 45 | cp -R /tmp/artifacts/packages/* $WARPDRIVE_SRC_ROOT/.warpdrive/packages 46 | fi 47 | 48 | echo "---> Installing application source" 49 | 50 | cp -Rf /tmp/src/. $WARPDRIVE_SRC_ROOT 51 | 52 | rm -rf /tmp/src 53 | 54 | # Trigger warpdrive to install any required Python packages and run any 55 | # hook scripts supplied by an application for the build. Exactly what is 56 | # run will depend on the build target. The default is to build the 57 | # application. The 'wheelhouse' target is to build Python wheels from 58 | # the required packages but not actually install them. 59 | 60 | WARPDRIVE_BUILD_SYSTEM=${WARPDRIVE_BUILD_SYSTEM:-docker} 61 | WARPDRIVE_BUILD_TARGET=${WARPDRIVE_BUILD_TARGET:-application} 62 | 63 | export WARPDRIVE_BUILD_SYSTEM 64 | export WARPDRIVE_BUILD_TARGET 65 | 66 | if [ "$WARPDRIVE_BUILD_TARGET" = "wheelhouse" ]; then 67 | echo "---> Building Python wheels for packages" 68 | 69 | warpdrive wheels 70 | else 71 | echo "---> Building application from source" 72 | 73 | warpdrive build 74 | fi 75 | 76 | # Need to make everything group writable so that 'oc rsync' will work 77 | # when deploying the image to OpenShift and trying to do live updates in 78 | # a running container. This means we are even making files which are not 79 | # writable by the owner writable by the group, but this is the only way 80 | # to make it work when running container as an arbitrary user ID and 81 | # relying on group access controls. 82 | 83 | echo "---> Fix permissions on application source" 84 | 85 | warpdrive fixup $WARPDRIVE_APP_ROOT 86 | -------------------------------------------------------------------------------- /s2i/bin/run: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ x"$WARPDRIVE_DEBUG" != x"" ]; then 4 | set -x 5 | fi 6 | 7 | set -eo pipefail 8 | 9 | # Root directory for the runtime environment of the application. 10 | 11 | WARPDRIVE_APP_ROOT=/opt/app-root 12 | 13 | # Check for any 'run' script which may have been added into the image by 14 | # an initial S2I build with the resulting image being used as a new S2I 15 | # builder. When one exists we execute it, with it overriding this 16 | # script. 17 | 18 | if [ x"$WARPDRIVE_S2I_PHASE" = x"" ]; then 19 | WARPDRIVE_S2I_PHASE=run 20 | export WARPDRIVE_S2I_PHASE 21 | 22 | if [ -x $WARPDRIVE_APP_ROOT/.s2i/bin/run ]; then 23 | exec $WARPDRIVE_APP_ROOT/.s2i/bin/run 24 | fi 25 | fi 26 | 27 | # Trigger warpdrive to start the application. We need to make sure we do 28 | # this using exec so that process ID '1' is inherited if we were started 29 | # up as process ID '1' in the Docker container. 30 | 31 | echo "---> Executing the start up script" 32 | 33 | exec warpdrive start 34 | -------------------------------------------------------------------------------- /s2i/bin/save-artifacts: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Root directory for the runtime environment of the application. 4 | 5 | WARPDRIVE_APP_ROOT=/opt/app-root 6 | 7 | # Check for any 'save-artifacts' script which may have been added into 8 | # the image by an initial S2I build with the resulting image being used 9 | # as a new S2I builder. When one exists we execute it, with it 10 | # overriding this script. 11 | 12 | if [ x"$WARPDRIVE_S2I_PHASE" = x"" ]; then 13 | WARPDRIVE_S2I_PHASE=save-artifacts 14 | export WARPDRIVE_S2I_PHASE 15 | 16 | if [ -x $WARPDRIVE_APP_ROOT/.s2i/bin/save-artifacts ]; then 17 | exec $WARPDRIVE_APP_ROOT/.s2i/bin/save-artifacts 18 | fi 19 | fi 20 | 21 | # If there was a wheelhouse directory then output it as saved artifacts. 22 | 23 | if [ -d /opt/app-root/src/.warpdrive/wheelhouse ]; then 24 | tar -c -C /opt/app-root/src/.warpdrive -f - wheelhouse packages 25 | fi 26 | -------------------------------------------------------------------------------- /s2i/bin/usage: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -eo pipefail 4 | 5 | # Root directory for the runtime environment of the application. 6 | 7 | WARPDRIVE_APP_ROOT=/opt/app-root 8 | 9 | # Check for any 'usage' script which may have been added into the image 10 | # by an initial S2I build with the resulting image being used as a new 11 | # S2I builder. When one exists we execute it, with it overriding this 12 | # script. 13 | 14 | if [ x"$WARPDRIVE_S2I_PHASE" = x"" ]; then 15 | WARPDRIVE_S2I_PHASE=usage 16 | export WARPDRIVE_S2I_PHASE 17 | 18 | if [ -x $WARPDRIVE_APP_ROOT/.s2i/bin/usage ]; then 19 | exec $WARPDRIVE_APP_ROOT/.s2i/bin/usage 20 | fi 21 | fi 22 | 23 | # Output usage. 24 | 25 | PROGRAM=`basename $0` 26 | 27 | PYTHON_VERSION_MN=`echo $PYTHON_VERSION | sed -e 's/\.[^.]*$//'` 28 | BUILDER_NAME="warp0-python$PYTHON_VERSION_MN-debian8" 29 | 30 | cat < $BUILDER_NAME 40 | 41 | The resulting image can then be run as: 42 | 43 | docker run -p 8080:8080 44 | 45 | The S2I builder is also compatible with the builtin support of OpenShift 3 46 | for deploying applications using S2I. 47 | EOF 48 | -------------------------------------------------------------------------------- /s2i/bootstrap/assemble: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ x"$WARPDRIVE_DEBUG" != x"" ]; then 4 | set -x 5 | env 6 | fi 7 | 8 | if [ -x /usr/local/s2i/bin/assemble ]; then 9 | WARPDRIVE_S2I_ROOT=/usr/local/s2i 10 | export WARPDRIVE_S2I_ROOT 11 | 12 | exec /usr/local/s2i/bin/assemble 13 | fi 14 | 15 | if [ -x /opt/app-root/s2i/bin/assemble ]; then 16 | WARPDRIVE_S2I_ROOT=/opt/app-root/s2i 17 | export WARPDRIVE_S2I_ROOT 18 | 19 | exec /opt/app-root/s2i/bin/assemble 20 | fi 21 | 22 | curl -SL -o /opt/app-root/getwarped.sh \ 23 | https://raw.githubusercontent.com/GrahamDumpleton/warpdrive/master/getwarped.sh 24 | 25 | WARPDRIVE_VERSION=${WARPDRIVE_VERSION:-0} 26 | 27 | if [ x"$WARPDRIVE_DEBUG" != x"" ]; then 28 | sh -x /opt/app-root/getwarped.sh --version $WARPDRIVE_VERSION 29 | else 30 | sh /opt/app-root/getwarped.sh --version $WARPDRIVE_VERSION 31 | fi 32 | 33 | WARPDRIVE_S2I_ROOT=/opt/app-root/s2i 34 | export WARPDRIVE_S2I_ROOT 35 | 36 | exec /opt/app-root/s2i/bin/assemble 37 | -------------------------------------------------------------------------------- /s2i/bootstrap/run: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ x"$WARPDRIVE_DEBUG" != x"" ]; then 4 | set -x 5 | fi 6 | 7 | if [ -x /usr/local/s2i/bin/run ]; then 8 | WARPDRIVE_S2I_ROOT=/usr/local/s2i 9 | export WARPDRIVE_S2I_ROOT 10 | 11 | exec /usr/local/s2i/bin/run 12 | fi 13 | 14 | WARPDRIVE_S2I_ROOT=/opt/app-root/s2i 15 | export WARPDRIVE_S2I_ROOT 16 | 17 | exec /opt/app-root/s2i/bin/run 18 | -------------------------------------------------------------------------------- /s2i/bootstrap/save-artifacts: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | if [ x"$WARPDRIVE_DEBUG" != x"" ]; then 4 | set -x 5 | fi 6 | 7 | if [ -x /usr/local/s2i/bin/save-artifacts ]; then 8 | WARPDRIVE_S2I_ROOT=/usr/local/s2i 9 | export WARPDRIVE_S2I_ROOT 10 | 11 | exec /usr/local/s2i/bin/save-artifacts 12 | fi 13 | 14 | WARPDRIVE_S2I_ROOT=/opt/app-root/s2i 15 | export WARPDRIVE_S2I_ROOT 16 | 17 | exec /opt/app-root/s2i/bin/save-artifacts 18 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from setuptools import setup 4 | 5 | def _files(prefix): 6 | result = [] 7 | for root, dirs, files in os.walk(prefix, topdown=False): 8 | for name in files: 9 | if root == prefix: 10 | result.append(os.path.join(root[len(prefix):], name)) 11 | else: 12 | result.append(os.path.join(root[len(prefix)+1:], name)) 13 | return result 14 | 15 | long_description = open('README.rst').read() 16 | 17 | classifiers = [ 18 | 'Development Status :: 4 - Beta', 19 | 'License :: OSI Approved :: BSD License', 20 | 'Operating System :: MacOS :: MacOS X', 21 | 'Operating System :: POSIX :: Linux', 22 | 'Programming Language :: Python :: 3.6', 23 | 'Programming Language :: Python :: 3.7', 24 | 'Programming Language :: Python :: 3.8', 25 | 'Topic :: Internet :: WWW/HTTP :: WSGI', 26 | ] 27 | 28 | setup(name = 'warpdrive', 29 | version = '0.34.0', 30 | description = 'Launcher for Python web applications.', 31 | long_description=long_description, 32 | url = 'https://github.com/getwarped/warpdrive', 33 | author = 'Graham Dumpleton', 34 | author_email = 'Graham.Dumpleton@gmail.com', 35 | license = 'BSD', 36 | classifiers = classifiers, 37 | keywords='wgsi docker openshift', 38 | packages = ['warpdrive', 'warpdrive.etc'], 39 | package_data = {'warpdrive.etc': _files('warpdrive/etc')}, 40 | entry_points = {'console_scripts': ['warpdrive = warpdrive:main']}, 41 | ) 42 | -------------------------------------------------------------------------------- /warpdrive/__init__.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | 3 | version = '0.34.0' 4 | 5 | import os 6 | import sys 7 | 8 | root = os.path.dirname(__file__) 9 | scripts = os.path.join(root, 'etc') 10 | 11 | def main(*args): 12 | args = sys.argv 13 | 14 | # Skip program name. 15 | 16 | if len(args) >= 2: 17 | action = args[1] 18 | args = args[2:] 19 | else: 20 | action = 'help' 21 | args = [] 22 | 23 | program = 'warpdrive-%s' % action 24 | executable = os.path.join(scripts, program) 25 | 26 | if not os.path.exists(executable): 27 | print('Error: unknown command "%s" for "warpdrive"' % action, file=sys.stderr) 28 | print('Run "warpdrive help for usage.', file=sys.stderr) 29 | sys.exit(1) 30 | 31 | os.environ['WARPDRIVE_VERSION'] = version 32 | 33 | os.execl(executable, program, *args) 34 | -------------------------------------------------------------------------------- /warpdrive/bin/warpdrive: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ACTION=$1 4 | 5 | shift 6 | 7 | WARPDRIVE_START=`which $0` 8 | WARPDRIVE_SCRIPTS=`dirname $WARPDRIVE_START`/../etc 9 | WARPDRIVE_SCRIPTS=`realpath $WARPDRIVE_SCRIPTS` 10 | 11 | if [ -x $WARPDRIVE_SCRIPTS/warpdrive-$ACTION ]; then 12 | exec $WARPDRIVE_SCRIPTS/warpdrive-$ACTION "$@" 13 | fi 14 | 15 | exec $WARPDRIVE_SCRIPTS/warpdrive-help 16 | -------------------------------------------------------------------------------- /warpdrive/etc/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/getwarped/warpdrive/aabdcc39c4edcf02ebd32f26f0e8a3004eb989e0/warpdrive/etc/__init__.py -------------------------------------------------------------------------------- /warpdrive/etc/base-debian: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Record everything that is run from this script so appears in logs. 4 | 5 | set -x 6 | 7 | # Ensure that any failure within this script causes this script to fail 8 | # immediately. This eliminates the need to check individual statuses for 9 | # anything which is run and prematurely exit. Note that the feature of 10 | # bash to exit in this way isn't foolproof. Ensure that you heed any 11 | # advice in: 12 | # 13 | # http://mywiki.wooledge.org/BashFAQ/105 14 | # http://fvue.nl/wiki/Bash:_Error_handling 15 | # 16 | # and use best practices to ensure that failures are always detected. 17 | # Any user supplied scripts should also use this failure mode. 18 | 19 | set -eo pipefail 20 | 21 | # Set the umask to be '002' so that any files/directories created from 22 | # this point are group writable. This does rely on any applications or 23 | # installation scripts honouring the umask setting, which unfortunately 24 | # not all do. 25 | 26 | umask 002 27 | 28 | # Create directory where additional packags will be installed. 29 | 30 | WARPDRIVE_ROOT_DIR=${WARPDRIVE_ROOT_DIR:-/usr/local} 31 | 32 | mkdir -p $WARPDRIVE_ROOT_DIR 33 | 34 | # Validate that package version details are set in the Dockerfile. 35 | 36 | test ! -z "$PYTHON_VERSION" || exit 1 37 | test ! -z "$MOD_WSGI_VERSION" || exit 1 38 | 39 | # Download source code for packages and unpack them. 40 | 41 | WARPDRIVE_BUILD_DIR=${WARPDRIVE_BUILD_DIR:-/tmp/warpdrive-init.$$} 42 | 43 | mkdir -p $WARPDRIVE_BUILD_DIR 44 | 45 | curl -SL -o $WARPDRIVE_BUILD_DIR/python.tar.gz https://www.python.org/ftp/python/$PYTHON_VERSION/Python-$PYTHON_VERSION.tgz 46 | 47 | mkdir $WARPDRIVE_BUILD_DIR/python 48 | 49 | tar -xC $WARPDRIVE_BUILD_DIR/python --strip-components=1 -f $WARPDRIVE_BUILD_DIR/python.tar.gz 50 | 51 | # To be safe, force the C compiler to be used to be the 64 bit compiler. 52 | 53 | CC=x86_64-linux-gnu-gcc 54 | export CC 55 | 56 | # Build Python from source code. Configure options used in part mirror 57 | # what is used by Debian itself when it builds its own Python packages. 58 | # We first install with a shared Python library, and then install the 59 | # static library and statically linked 'python' executable. 60 | 61 | cd $WARPDRIVE_BUILD_DIR/python 62 | 63 | CONFIG_ARGS="--prefix=$WARPDRIVE_ROOT_DIR/python \ 64 | --enable-ipv6 --with-dbmliborder=bdb:gdbm --with-system-expat \ 65 | --with-system-ffi --with-fpectl" 66 | 67 | case "$PYTHON_VERSION" in 68 | 2.*) 69 | CONFIG_ARGS="$CONFIG_ARGS --enable-unicode=ucs4" 70 | ;; 71 | 3.[012].*) 72 | CONFIG_ARGS="$CONFIG_ARGS --with-wide-unicode" 73 | ;; 74 | esac 75 | 76 | ./configure $CONFIG_ARGS --enable-shared 77 | 78 | LD_RUN_PATH=$WARPDRIVE_ROOT_DIR/python/lib 79 | export LD_RUN_PATH 80 | 81 | make 82 | make install 83 | make distclean 84 | 85 | ./configure $CONFIG_ARGS 86 | 87 | make 88 | make altbininstall 89 | 90 | unset LD_RUN_PATH 91 | 92 | cd $WARPDRIVE_ROOT_DIR/python/bin 93 | 94 | case "$PYTHON_VERSION" in 95 | 3.*) 96 | ln -s python3 python 97 | ;; 98 | esac 99 | 100 | # Set PATH to include bin directories for Python and Apache. 101 | 102 | PATH="$WARPDRIVE_ROOT_DIR/python/bin:$PATH" 103 | export PATH 104 | 105 | # Install additional common Python packages we want installed. This 106 | # includes installing mod_wsgi-express as a fallback in case someone 107 | # expects it to be available as part of the base image. 108 | 109 | curl -SL 'https://bootstrap.pypa.io/get-pip.py' | python 110 | 111 | if [ ! -f $WARPDRIVE_ROOT_DIR/python/bin/pip ]; then 112 | if [ -f $WARPDRIVE_ROOT_DIR/python/bin/pip3 ]; then 113 | ln -s pip3 $WARPDRIVE_ROOT_DIR/python/bin/pip 114 | fi 115 | fi 116 | 117 | pip install --no-cache-dir virtualenv 118 | 119 | # Prune unwanted files from Python and Apache installations. 120 | 121 | find $WARPDRIVE_ROOT_DIR/python/lib \ 122 | \( -type d -and -name test -or -name tests \) -or \ 123 | \( -type f -and -name '*.pyc' -or -name '*.pyo' \) | \ 124 | xargs rm -rf 125 | 126 | # Fixup group ownership and directory/file permissions for installation 127 | # area so group writable by 'root'. 128 | 129 | chgrp -R root $WARPDRIVE_ROOT_DIR 130 | 131 | find $WARPDRIVE_ROOT_DIR -type d -exec chmod g+ws {} \; 132 | 133 | find $WARPDRIVE_ROOT_DIR -perm 2755 -exec chmod g+w {} \; 134 | find $WARPDRIVE_ROOT_DIR -perm 0644 -exec chmod g+w {} \; 135 | 136 | # Clean up the temporary build area. 137 | 138 | rm -rf $WARPDRIVE_BUILD_DIR 139 | 140 | # Create a special user account under which any application should be 141 | # run. This has group of 'root' with gid of '0' as it appears to be 142 | # safest bet to allow file system permisions for everything to work. 143 | 144 | WARPDRIVE_APP_ROOT=${WARPDRIVE_APP_ROOT:-/opt/app-root} 145 | 146 | adduser --disabled-password --gecos "Warp Drive" --uid 1001 --gid 0 \ 147 | --home ${WARPDRIVE_APP_ROOT} default 148 | 149 | chmod g+ws ${WARPDRIVE_APP_ROOT} 150 | 151 | # Create directories to be used as application directory etc. Ensure 152 | # they are group writable, as we cannot be certain what uid application 153 | # will run as. We do expect gid of '0' to be used though. 154 | 155 | mkdir -p ${WARPDRIVE_APP_ROOT}/tmp 156 | chmod g+ws ${WARPDRIVE_APP_ROOT}/tmp 157 | 158 | WARPDRIVE_SRC_ROOT=${WARPDRIVE_SRC_ROOT:-$WARPDRIVE_APP_ROOT/src} 159 | 160 | mkdir -p ${WARPDRIVE_SRC_ROOT} 161 | chmod g+ws ${WARPDRIVE_SRC_ROOT} 162 | 163 | # Create a Python virtual environment and ensure pip is up to date. 164 | # Also install mod_wsgi as the default WSGI server. 165 | 166 | virtualenv $WARPDRIVE_APP_ROOT 167 | 168 | . $WARPDRIVE_APP_ROOT/bin/activate 169 | 170 | pip install --no-cache-dir -U pip 171 | pip install --no-cache-dir mod_wsgi==$MOD_WSGI_VERSION 172 | 173 | find $WARPDRIVE_APP_ROOT -type d -exec chmod g+ws {} \; 174 | 175 | find $WARPDRIVE_APP_ROOT -perm 2755 -exec chmod g+w {} \; 176 | find $WARPDRIVE_APP_ROOT -perm 0644 -exec chmod g+w {} \; 177 | -------------------------------------------------------------------------------- /warpdrive/etc/shell-init: -------------------------------------------------------------------------------- 1 | unset BASH_ENV PROMPT_COMMAND ENV 2 | 3 | eval $(warpdrive env) 4 | -------------------------------------------------------------------------------- /warpdrive/etc/shell-rcfile: -------------------------------------------------------------------------------- 1 | function warpdrive() { 2 | case $1 in 3 | project) 4 | shift 5 | 6 | WARPDRIVE_ACTIVATE_ARGS="" 7 | 8 | WARPDRIVE_VIRTUALENVS=${WARPDRIVE_VIRTUALENVS:-$HOME/.warpdrive} 9 | 10 | WARPDRIVE_CREATE_PROJECT=false 11 | 12 | WARPDRIVE_ENV_NAME="default" 13 | 14 | while [ "$#" != "0" ]; do 15 | case "$1" in 16 | --create) 17 | WARPDRIVE_CREATE_PROJECT=true 18 | ;; 19 | 20 | --python=*) 21 | WARPDRIVE_PYTHON_BINARY=`echo $1 | sed -e 's/--python=//'` 22 | WARPDRIVE_ACTIVATE_ARGS="$WARPDRIVE_ACTIVATE_ARGS --python=$WARPDRIVE_PYTHON_BINARY" 23 | ;; 24 | --python) 25 | WARPDRIVE_PYTHON_BINARY=$2 26 | WARPDRIVE_ACTIVATE_ARGS="$WARPDRIVE_ACTIVATE_ARGS --python=$WARPDRIVE_PYTHON_BINARY" 27 | shift 28 | ;; 29 | 30 | *) 31 | WARPDRIVE_ENV_NAME=$1 32 | ;; 33 | esac 34 | 35 | shift 36 | done 37 | 38 | WARPDRIVE_VENV_DIR="$WARPDRIVE_VIRTUALENVS/warpdrive+$WARPDRIVE_ENV_NAME" 39 | 40 | if [ -d "$WARPDRIVE_VENV_DIR" ]; then 41 | echo "Switching to warpdrive project '$WARPDRIVE_ENV_NAME'." 42 | else 43 | if [ x"$WARPDRIVE_CREATE_PROJECT" == x"true" ]; then 44 | echo "Initializing warpdrive project '$WARPDRIVE_ENV_NAME'." 45 | else 46 | echo "Project '$WARPDRIVE_ENV_NAME' does not exist. Use the --create option to create it." 47 | 48 | return 49 | fi 50 | fi 51 | 52 | eval "$($WARPDRIVE activate $WARPDRIVE_ENV_NAME $WARPDRIVE_ACTIVATE_ARGS)" 53 | 54 | ;; 55 | 56 | deactivate) 57 | if [ x"$VIRTUAL_ENV" != x"" ]; then 58 | if [ x`type -t deactivate` = x"function" ]; then 59 | WARPDRIVE=$WARPDRIVE_MASTER 60 | unset WARPDRIVE_MASTER 61 | unset WARPDRIVE_ENV_NAME 62 | unset WARPDRIVE_APP_ROOT 63 | unset WARPDRIVE_SRC_ROOT 64 | deactivate 65 | fi 66 | fi 67 | 68 | ;; 69 | 70 | *) 71 | $WARPDRIVE $* 72 | 73 | ;; 74 | esac 75 | } 76 | -------------------------------------------------------------------------------- /warpdrive/etc/start-gunicorn: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ x"$WARPDRIVE_DEBUG" != x"" ]; then 4 | set -x 5 | fi 6 | 7 | WARPDRIVE_SERVER_ARGS="--log-file - --bind 0.0.0.0:$WARPDRIVE_HTTP_PORT" 8 | 9 | if [ -d .warpdrive/server_args ]; then 10 | WARPDRIVE_SERVER_ARGS_FILE=".warpdrive/server_args/gunicorn" 11 | else 12 | WARPDRIVE_SERVER_ARGS_FILE=".warpdrive/server_args" 13 | fi 14 | 15 | if [ -f $WARPDRIVE_SERVER_ARGS_FILE ]; then 16 | WARPDRIVE_SERVER_ARGS="$WARPDRIVE_SERVER_ARGS `cat $WARPDRIVE_SERVER_ARGS_FILE`" 17 | 18 | # Expand any environment variable references in options. 19 | 20 | WARPDRIVE_TMP_FILE=/tmp/server_args.$$ 21 | 22 | cat > $WARPDRIVE_TMP_FILE << EOF 23 | #!/bin/sh 24 | cat << ! 25 | $WARPDRIVE_SERVER_ARGS 26 | ! 27 | EOF 28 | 29 | chmod +x $WARPDRIVE_TMP_FILE 30 | 31 | WARPDRIVE_SERVER_ARGS=`$WARPDRIVE_TMP_FILE | tr '\n' ' '` 32 | 33 | rm -f $WARPDRIVE_TMP_FILE 34 | fi 35 | 36 | echo " -----> Executing server command 'gunicorn $WARPDRIVE_SERVER_ARGS $@'" 37 | 38 | exec warpdrive exec gunicorn $WARPDRIVE_SERVER_ARGS "$@" 39 | -------------------------------------------------------------------------------- /warpdrive/etc/start-mod_wsgi: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | if [ x"$WARPDRIVE_DEBUG" != x"" ]; then 4 | set -x 5 | fi 6 | 7 | # Now run the the actual application under Apache/mod_wsgi. This is run 8 | # in the foreground, replacing this process and adopting process ID 1 so 9 | # that signals are received properly and Apache will shutdown properly 10 | # when the container is being stopped. It will log to stdout/stderr. 11 | # 12 | # In running the mod_wsgi-express command, we collect select override 13 | # arguments from the environment. We also allow extra server arguments 14 | # in the file '.warpdrive/server_args'. 15 | 16 | WARPDRIVE_SERVER_ARGS="--server-root $WARPDRIVE_APP_ROOT/mod_wsgi" 17 | WARPDRIVE_SERVER_ARGS="$WARPDRIVE_SERVER_ARGS --log-to-terminal --startup-log" 18 | WARPDRIVE_SERVER_ARGS="$WARPDRIVE_SERVER_ARGS --port $WARPDRIVE_HTTP_PORT" 19 | 20 | if [ x"$MOD_WSGI_ACCESS_LOG" != x"" ]; then 21 | WARPDRIVE_SERVER_ARGS="$WARPDRIVE_SERVER_ARGS --access-log" 22 | fi 23 | 24 | if [ x"$MOD_WSGI_PROCESSES" != x"" ]; then 25 | WARPDRIVE_SERVER_ARGS="$WARPDRIVE_SERVER_ARGS --processes $MOD_WSGI_PROCESSES" 26 | fi 27 | 28 | if [ x"$MOD_WSGI_THREADS" != x"" ]; then 29 | WARPDRIVE_SERVER_ARGS="$WARPDRIVE_SERVER_ARGS --threads $MOD_WSGI_THREADS" 30 | fi 31 | 32 | if [ x"$MOD_WSGI_MAX_CLIENTS" != x"" ]; then 33 | WARPDRIVE_SERVER_ARGS="$WARPDRIVE_SERVER_ARGS --max-clients $MOD_WSGI_MAX_CLIENTS" 34 | fi 35 | 36 | if [ x"$MOD_WSGI_INITIAL_WORKERS" != x"" ]; then 37 | WARPDRIVE_SERVER_ARGS="$WARPDRIVE_SERVER_ARGS --initial-workers $MOD_WSGI_INITIAL_WORKERS" 38 | fi 39 | 40 | if [ x"$MOD_WSGI_MINIMUM_SPARE_WORKERS" != x"" ]; then 41 | WARPDRIVE_SERVER_ARGS="$WARPDRIVE_SERVER_ARGS --minimum-spare-workers $MOD_WSGI_MINIMUM_SPARE_WORKERS" 42 | fi 43 | 44 | if [ x"$MOD_WSGI_MAXIMUM_SPARE_WORKERS" != x"" ]; then 45 | WARPDRIVE_SERVER_ARGS="$WARPDRIVE_SERVER_ARGS --maximum-spare-workers $MOD_WSGI_MAXIMUM_SPARE_WORKERS" 46 | fi 47 | 48 | if [ x"$MOD_WSGI_LIMIT_REQUEST_BODY" != x"" ]; then 49 | WARPDRIVE_SERVER_ARGS="$WARPDRIVE_SERVER_ARGS --limit-request-body $MOD_WSGI_LIMIT_REQUEST_BODY" 50 | fi 51 | 52 | if [ x"$MOD_WSGI_MAXIMUM_REQUESTS" != x"" ]; then 53 | WARPDRIVE_SERVER_ARGS="$WARPDRIVE_SERVER_ARGS --maximum-requests $MOD_WSGI_MAXIMUM_REQUESTS" 54 | fi 55 | 56 | if [ x"$MOD_WSGI_INACTIVITY_TIMEOUT" != x"" ]; then 57 | WARPDRIVE_SERVER_ARGS="$WARPDRIVE_SERVER_ARGS --inactivity-timeout $MOD_WSGI_INACTIVITY_TIMEOUT" 58 | fi 59 | 60 | if [ x"$MOD_WSGI_REQUEST_TIMEOUT" != x"" ]; then 61 | WARPDRIVE_SERVER_ARGS="$WARPDRIVE_SERVER_ARGS --request-timeout $MOD_WSGI_REQUEST_TIMEOUT" 62 | fi 63 | 64 | if [ x"$MOD_WSGI_CONNECT_TIMEOUT" != x"" ]; then 65 | WARPDRIVE_SERVER_ARGS="$WARPDRIVE_SERVER_ARGS --connect-timeout $MOD_WSGI_CONNECT_TIMEOUT" 66 | fi 67 | 68 | if [ x"$MOD_WSGI_SOCKET_TIMEOUT" != x"" ]; then 69 | WARPDRIVE_SERVER_ARGS="$WARPDRIVE_SERVER_ARGS --socket-timeout $MOD_WSGI_SOCKET_TIMEOUT" 70 | fi 71 | 72 | if [ x"$MOD_WSGI_QUEUE_TIMEOUT" != x"" ]; then 73 | WARPDRIVE_SERVER_ARGS="$WARPDRIVE_SERVER_ARGS --queue-timeout $MOD_WSGI_QUEUE_TIMEOUT" 74 | fi 75 | 76 | if [ x"$MOD_WSGI_HEADER_TIMEOUT" != x"" ]; then 77 | WARPDRIVE_SERVER_ARGS="$WARPDRIVE_SERVER_ARGS --header-timeout $MOD_WSGI_HEADER_TIMEOUT" 78 | fi 79 | 80 | if [ x"$MOD_WSGI_HEADER_MAX_TIMEOUT" != x"" ]; then 81 | WARPDRIVE_SERVER_ARGS="$WARPDRIVE_SERVER_ARGS --header-max-timeout $MOD_WSGI_HEADER_MAX_TIMEOUT" 82 | fi 83 | 84 | if [ x"$MOD_WSGI_HEADER_MIN_RATE" != x"" ]; then 85 | WARPDRIVE_SERVER_ARGS="$WARPDRIVE_SERVER_ARGS --header-min-rate $MOD_WSGI_HEADER_MIN_RATE" 86 | fi 87 | 88 | if [ x"$MOD_WSGI_BODY_TIMEOUT" != x"" ]; then 89 | WARPDRIVE_SERVER_ARGS="$WARPDRIVE_SERVER_ARGS --body-timeout $MOD_WSGI_BODY_TIMEOUT" 90 | fi 91 | 92 | if [ x"$MOD_WSGI_BODY_MAX_TIMEOUT" != x"" ]; then 93 | WARPDRIVE_SERVER_ARGS="$WARPDRIVE_SERVER_ARGS --body-max-timeout $MOD_WSGI_BODY_MAX_TIMEOUT" 94 | fi 95 | 96 | if [ x"$MOD_WSGI_BODY_MIN_RATE" != x"" ]; then 97 | WARPDRIVE_SERVER_ARGS="$WARPDRIVE_SERVER_ARGS --body-min-rate $MOD_WSGI_BODY_MIN_RATE" 98 | fi 99 | 100 | if [ x"$MOD_WSGI_SERVER_BACKLOG" != x"" ]; then 101 | WARPDRIVE_SERVER_ARGS="$WARPDRIVE_SERVER_ARGS --server-backlog $MOD_WSGI_SERVER_BACKLOG" 102 | fi 103 | 104 | if [ x"$MOD_WSGI_DAEMON_BACKLOG" != x"" ]; then 105 | WARPDRIVE_SERVER_ARGS="$WARPDRIVE_SERVER_ARGS --daemon-backlog $MOD_WSGI_DAEMON_BACKLOG" 106 | fi 107 | 108 | if [ x"$MOD_WSGI_SERVER_MPM" != x"" ]; then 109 | WARPDRIVE_SERVER_ARGS="$WARPDRIVE_SERVER_ARGS --server-mpm $MOD_WSGI_SERVER_MPM" 110 | fi 111 | 112 | if [ x"$MOD_WSGI_LOG_LEVEL" != x"" ]; then 113 | WARPDRIVE_SERVER_ARGS="$WARPDRIVE_SERVER_ARGS --log-level $MOD_WSGI_LOG_LEVEL" 114 | fi 115 | 116 | if [ x"$MOD_WSGI_RELOAD_ON_CHANGES" != x"" ]; then 117 | WARPDRIVE_SERVER_ARGS="$WARPDRIVE_SERVER_ARGS --reload-on-changes" 118 | fi 119 | 120 | if [ x"$MOD_WSGI_ENABLE_DEBUGGER" != x"" ]; then 121 | WARPDRIVE_SERVER_ARGS="$WARPDRIVE_SERVER_ARGS --debug-mode --enable-debugger" 122 | fi 123 | 124 | if [ x"$MOD_WSGI_WORKING_DIRECTORY" != x"" ]; then 125 | WARPDRIVE_SERVER_ARGS="$WARPDRIVE_SERVER_ARGS --working-directory $MOD_WSGI_WORKING_DIRECTORY" 126 | fi 127 | 128 | if [ x"$MOD_WSGI_APPLICATION_TYPE" != x"" ]; then 129 | WARPDRIVE_SERVER_ARGS="$WARPDRIVE_SERVER_ARGS --application-type $MOD_WSGI_APPLICATION_TYPE" 130 | fi 131 | 132 | if [ x"$MOD_WSGI_ENTRY_POINT" != x"" ]; then 133 | WARPDRIVE_SERVER_ARGS="$WARPDRIVE_SERVER_ARGS --entry-point $MOD_WSGI_ENTRY_POINT" 134 | fi 135 | 136 | if [ x"$MOD_WSGI_CALLABLE_OBJECT" != x"" ]; then 137 | WARPDRIVE_SERVER_ARGS="$WARPDRIVE_SERVER_ARGS --callable-object $MOD_WSGI_CALLABLE_OBJECT" 138 | fi 139 | 140 | if [ x"$MOD_WSGI_SERVER_STATUS" != x"" ]; then 141 | WARPDRIVE_SERVER_ARGS="$WARPDRIVE_SERVER_ARGS --server-status" 142 | fi 143 | 144 | if [ x"$NEW_RELIC_LICENSE_KEY" != x"" -o \ 145 | x"$NEW_RELIC_CONFIG_FILE" != x"" ]; then 146 | WARPDRIVE_SERVER_ARGS="$WARPDRIVE_SERVER_ARGS --with-newrelic" 147 | fi 148 | 149 | if [ -d .warpdrive/server_args ]; then 150 | WARPDRIVE_SERVER_ARGS_FILE=".warpdrive/server_args/mod_wsgi" 151 | else 152 | WARPDRIVE_SERVER_ARGS_FILE=".warpdrive/server_args" 153 | fi 154 | 155 | if [ -f $WARPDRIVE_SERVER_ARGS_FILE ]; then 156 | WARPDRIVE_SERVER_ARGS="$WARPDRIVE_SERVER_ARGS `cat $WARPDRIVE_SERVER_ARGS_FILE`" 157 | 158 | # Expand any environment variable references in options. 159 | 160 | WARPDRIVE_TMP_FILE=/tmp/server_args.$$ 161 | 162 | cat > $WARPDRIVE_TMP_FILE << EOF 163 | #!/bin/sh 164 | cat << ! 165 | $WARPDRIVE_SERVER_ARGS 166 | ! 167 | EOF 168 | 169 | chmod +x $WARPDRIVE_TMP_FILE 170 | 171 | WARPDRIVE_SERVER_ARGS=`$WARPDRIVE_TMP_FILE | tr '\n' ' '` 172 | 173 | rm -f $WARPDRIVE_TMP_FILE 174 | fi 175 | 176 | rm -f $WARPDRIVE_APP_ROOT/bin/apachectl 177 | ln -s $WARPDRIVE_APP_ROOT/mod_wsgi/apachectl $WARPDRIVE_APP_ROOT/bin/apachectl 178 | 179 | echo " -----> Executing server command 'mod_wsgi-express start-server $WARPDRIVE_SERVER_ARGS $@'" 180 | exec warpdrive exec mod_wsgi-express start-server $WARPDRIVE_SERVER_ARGS "$@" 181 | -------------------------------------------------------------------------------- /warpdrive/etc/start-python: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ x"$WARPDRIVE_DEBUG" != x"" ]; then 4 | set -x 5 | fi 6 | 7 | WARPDRIVE_SCRIPT=$1 8 | 9 | WARPDRIVE_SERVER_ARGS="" 10 | 11 | if [ -d .warpdrive/server_args ]; then 12 | WARPDRIVE_SERVER_ARGS_FILE=".warpdrive/server_args/python" 13 | else 14 | WARPDRIVE_SERVER_ARGS_FILE=".warpdrive/server_args" 15 | fi 16 | 17 | if [ -f $WARPDRIVE_SERVER_ARGS_FILE ]; then 18 | WARPDRIVE_SERVER_ARGS="$WARPDRIVE_SERVER_ARGS `cat $WARPDRIVE_SERVER_ARGS_FILE`" 19 | 20 | # Expand any environment variable references in options. 21 | 22 | WARPDRIVE_TMP_FILE=/tmp/server_args.$$ 23 | 24 | cat > $WARPDRIVE_TMP_FILE << EOF 25 | #!/bin/sh 26 | cat << ! 27 | $WARPDRIVE_SERVER_ARGS 28 | ! 29 | EOF 30 | 31 | chmod +x $WARPDRIVE_TMP_FILE 32 | 33 | WARPDRIVE_SERVER_ARGS=`$WARPDRIVE_TMP_FILE | tr '\n' ' '` 34 | 35 | rm -f $WARPDRIVE_TMP_FILE 36 | fi 37 | 38 | echo " -----> Executing server command 'python $WARPDRIVE_SCRIPT $WARPDRIVE_SERVER_ARGS'" 39 | 40 | exec warpdrive exec python $WARPDRIVE_SCRIPT $WARPDRIVE_SERVER_ARGS 41 | -------------------------------------------------------------------------------- /warpdrive/etc/start-shell: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ x"$WARPDRIVE_DEBUG" != x"" ]; then 4 | set -x 5 | fi 6 | 7 | WARPDRIVE_PROGRAM=$1 8 | 9 | WARPDRIVE_SERVER_ARGS="" 10 | 11 | if [ -d .warpdrive/server_args ]; then 12 | WARPDRIVE_SERVER_ARGS_FILE=".warpdrive/server_args/shell" 13 | else 14 | WARPDRIVE_SERVER_ARGS_FILE=".warpdrive/server_args" 15 | fi 16 | 17 | if [ -f $WARPDRIVE_SERVER_ARGS_FILE ]; then 18 | WARPDRIVE_SERVER_ARGS="$WARPDRIVE_SERVER_ARGS `cat $WARPDRIVE_SERVER_ARGS_FILE`" 19 | 20 | # Expand any environment variable references in options. 21 | 22 | WARPDRIVE_TMP_FILE=/tmp/server_args.$$ 23 | 24 | cat > $WARPDRIVE_TMP_FILE << EOF 25 | #!/bin/sh 26 | cat << ! 27 | $WARPDRIVE_SERVER_ARGS 28 | ! 29 | EOF 30 | 31 | chmod +x $WARPDRIVE_TMP_FILE 32 | 33 | WARPDRIVE_SERVER_ARGS=`$WARPDRIVE_TMP_FILE | tr '\n' ' '` 34 | 35 | rm -f $WARPDRIVE_TMP_FILE 36 | fi 37 | 38 | echo " -----> Executing server command '$WARPDRIVE_PROGRAM $WARPDRIVE_SERVER_ARGS'" 39 | 40 | exec warpdrive exec $WARPDRIVE_PROGRAM $WARPDRIVE_SERVER_ARGS 41 | -------------------------------------------------------------------------------- /warpdrive/etc/start-uwsgi: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ x"$WARPDRIVE_DEBUG" != x"" ]; then 4 | set -x 5 | fi 6 | 7 | WARPDRIVE_SERVER_ARGS="--master --http-socket :$WARPDRIVE_HTTP_PORT" 8 | WARPDRIVE_SERVER_ARGS="$WARPDRIVE_SERVER_ARGS --enable-threads" 9 | WARPDRIVE_SERVER_ARGS="$WARPDRIVE_SERVER_ARGS --threads=5" 10 | WARPDRIVE_SERVER_ARGS="$WARPDRIVE_SERVER_ARGS --thunder-lock" 11 | WARPDRIVE_SERVER_ARGS="$WARPDRIVE_SERVER_ARGS --single-interpreter" 12 | WARPDRIVE_SERVER_ARGS="$WARPDRIVE_SERVER_ARGS --die-on-term" 13 | 14 | if [ -d .warpdrive/server_args ]; then 15 | WARPDRIVE_SERVER_ARGS_FILE=".warpdrive/server_args/uwsgi" 16 | else 17 | WARPDRIVE_SERVER_ARGS_FILE=".warpdrive/server_args" 18 | fi 19 | 20 | if [ -f $WARPDRIVE_SERVER_ARGS_FILE ]; then 21 | WARPDRIVE_SERVER_ARGS="$WARPDRIVE_SERVER_ARGS `cat $WARPDRIVE_SERVER_ARGS_FILE`" 22 | 23 | # Expand any environment variable references in options. 24 | 25 | WARPDRIVE_TMP_FILE=/tmp/server_args.$$ 26 | 27 | cat > $WARPDRIVE_TMP_FILE << EOF 28 | #!/bin/sh 29 | cat << ! 30 | $WARPDRIVE_SERVER_ARGS 31 | ! 32 | EOF 33 | 34 | chmod +x $WARPDRIVE_TMP_FILE 35 | 36 | WARPDRIVE_SERVER_ARGS=`$WARPDRIVE_TMP_FILE | tr '\n' ' '` 37 | 38 | rm -f $WARPDRIVE_TMP_FILE 39 | fi 40 | 41 | echo " -----> Executing server command 'uwsgi $WARPDRIVE_SERVER_ARGS $@'" 42 | 43 | exec warpdrive exec uwsgi $WARPDRIVE_SERVER_ARGS "$@" 44 | -------------------------------------------------------------------------------- /warpdrive/etc/start-waitress: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ x"$WARPDRIVE_DEBUG" != x"" ]; then 4 | set -x 5 | fi 6 | 7 | WARPDRIVE_SERVER_ARGS="--port $WARPDRIVE_HTTP_PORT" 8 | WARPDRIVE_SERVER_ARGS="$WARPDRIVE_SERVER_ARGS --threads=5" 9 | 10 | if [ -d .warpdrive/server_args ]; then 11 | WARPDRIVE_SERVER_ARGS_FILE=".warpdrive/server_args/waitress" 12 | else 13 | WARPDRIVE_SERVER_ARGS_FILE=".warpdrive/server_args" 14 | fi 15 | 16 | if [ -f $WARPDRIVE_SERVER_ARGS_FILE ]; then 17 | WARPDRIVE_SERVER_ARGS="$WARPDRIVE_SERVER_ARGS `cat $WARPDRIVE_SERVER_ARGS_FILE`" 18 | 19 | # Expand any environment variable references in options. 20 | 21 | WARPDRIVE_TMP_FILE=/tmp/server_args.$$ 22 | 23 | cat > $WARPDRIVE_TMP_FILE << EOF 24 | #!/bin/sh 25 | cat << ! 26 | $WARPDRIVE_SERVER_ARGS 27 | ! 28 | EOF 29 | 30 | chmod +x $WARPDRIVE_TMP_FILE 31 | 32 | WARPDRIVE_SERVER_ARGS=`$WARPDRIVE_TMP_FILE | tr '\n' ' '` 33 | 34 | rm -f $WARPDRIVE_TMP_FILE 35 | fi 36 | 37 | echo " -----> Executing server command 'waitress-serve $WARPDRIVE_SERVER_ARGS $@'" 38 | 39 | exec warpdrive exec waitress-serve $WARPDRIVE_SERVER_ARGS "$@" 40 | -------------------------------------------------------------------------------- /warpdrive/etc/warpdrive-activate: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Has warpdrive been run before? If it hasn't, then create directory 4 | # where our virtual Python environments will live. 5 | 6 | WARPDRIVE_VIRTUALENVS=${WARPDRIVE_VIRTUALENVS:-$HOME/.warpdrive} 7 | 8 | test ! -d $WARPDRIVE_VIRTUALENVS && mkdir $WARPDRIVE_VIRTUALENVS 9 | 10 | # What is the name of the supplied Python virtualenv environment to 11 | # be activated. Does it already exist? If it doesn't exist then we 12 | # need to create it and initialise it with base packages. 13 | 14 | WARPDRIVE_VIRTUALENV_ARGS="" 15 | 16 | WARPDRIVE_ENV_NAME="default" 17 | 18 | while [ "$#" != "0" ]; do 19 | case "$1" in 20 | --python=*) 21 | WARPDRIVE_PYTHON_BINARY=`echo $1 | sed -e 's/--python=//'` 22 | WARPDRIVE_VIRTUALENV_ARGS="$WARPDRIVE_VIRTUALENV_ARGS --python=$WARPDRIVE_PYTHON_BINARY" 23 | ;; 24 | --python) 25 | WARPDRIVE_PYTHON_BINARY=$2 26 | WARPDRIVE_VIRTUALENV_ARGS="$WARPDRIVE_VIRTUALENV_ARGS --python=$WARPDRIVE_PYTHON_BINARY" 27 | shift 28 | ;; 29 | 30 | *) 31 | WARPDRIVE_ENV_NAME=$1 32 | ;; 33 | esac 34 | 35 | shift 36 | done 37 | 38 | WARPDRIVE_VENV_DIR="$WARPDRIVE_VIRTUALENVS/warpdrive+$WARPDRIVE_ENV_NAME" 39 | 40 | create_environment() { 41 | virtualenv $WARPDRIVE_VIRTUALENV_ARGS $WARPDRIVE_VENV_DIR 42 | 43 | source $WARPDRIVE_VENV_DIR/bin/activate 44 | 45 | if [ x"$WARPDRIVE_PACKAGE_URL" == x"" ]; then 46 | pip install warpdrive==$WARPDRIVE_VERSION 47 | else 48 | pip install $WARPDRIVE_PACKAGE_URL 49 | fi 50 | 51 | mkdir $WARPDRIVE_VENV_DIR/tmp 52 | } 53 | 54 | if [ ! -d $WARPDRIVE_VENV_DIR ]; 55 | then 56 | create_environment > $WARPDRIVE_VIRTUALENVS/warpdrive-activate.log 57 | fi 58 | 59 | WARPDRIVE_ACTIVATE="$WARPDRIVE_VENV_DIR/bin/activate" 60 | 61 | WARPDRIVE_MASTER=${WARPDRIVE_MASTER:-$WARPDRIVE} 62 | WARPDRIVE=$WARPDRIVE_VENV_DIR/bin/warpdrive 63 | WARPDRIVE_APP_ROOT=$WARPDRIVE_VENV_DIR 64 | WARPDRIVE_SRC_ROOT=`pwd` 65 | 66 | echo "export WARPDRIVE=$WARPDRIVE" 67 | echo "export WARPDRIVE_ENV_NAME=$WARPDRIVE_ENV_NAME" 68 | echo "export WARPDRIVE_APP_ROOT=$WARPDRIVE_APP_ROOT" 69 | echo "export WARPDRIVE_SRC_ROOT=$WARPDRIVE_SRC_ROOT" 70 | echo "export WARPDRIVE_MASTER=$WARPDRIVE_MASTER" 71 | 72 | echo "source $WARPDRIVE_ACTIVATE" 73 | echo "# Run this command to configure your shell:" 74 | echo "# eval \"\$(warpdrive activate $WARPDRIVE_ENV_NAME)\"" 75 | -------------------------------------------------------------------------------- /warpdrive/etc/warpdrive-alive: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ x"$WARPDRIVE_DEBUG" != x"" ]; then 4 | set -x 5 | fi 6 | 7 | # Ensure that any failure within this script or a user provided script 8 | # causes this script to fail immediately. This eliminates the need to 9 | # check individual statuses for anything which is run and prematurely 10 | # exit. Note that the feature of bash to exit in this way isn't 11 | # foolproof. Ensure that you heed any advice in: 12 | # 13 | # http://mywiki.wooledge.org/BashFAQ/105 14 | # http://fvue.nl/wiki/Bash:_Error_handling 15 | # 16 | # and use best practices to ensure that failures are always detected. 17 | # Any user supplied scripts should also use this failure mode. 18 | 19 | set -eo pipefail 20 | 21 | # Setup the environment if not already done. 22 | 23 | if [ x"$WARPDRIVE_ACTION" = x"" ]; then 24 | eval "$(warpdrive env)" 25 | fi 26 | 27 | WARPDRIVE_ACTION=alive 28 | export WARPDRIVE_ACTION 29 | 30 | # Make sure we are in the correct working directory for the application. 31 | 32 | cd $WARPDRIVE_SRC_ROOT 33 | 34 | # Run any user supplied script to test whether the deployed application 35 | # is alive. The script must be executable in order to be run. It is not 36 | # possible for this script to change the permissions so it is executable 37 | # and then run it, due to some docker bug which results in the text file 38 | # being busy. For more details see: 39 | # 40 | # https://github.com/docker/docker/issues/9547 41 | 42 | if [ -f .warpdrive/action_hooks/alive ]; then 43 | if [ ! -x .warpdrive/action_hooks/alive ]; then 44 | echo "WARNING: Script .warpdrive/action_hooks/alive not executable." 45 | fi 46 | fi 47 | 48 | if [ -x .warpdrive/action_hooks/alive ]; then 49 | echo " -----> Running .warpdrive/action_hooks/alive" 50 | exec .warpdrive/action_hooks/alive 51 | fi 52 | -------------------------------------------------------------------------------- /warpdrive/etc/warpdrive-base: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ x"$WARPDRIVE_DEBUG" != x"" ]; then 4 | set -x 5 | fi 6 | 7 | # This script will trigger installation of Python in base image. 8 | 9 | WARPDRIVE_BASE=`which $0` 10 | WARPDRIVE_SCRIPTS=`dirname $WARPDRIVE_BASE` 11 | 12 | WARPDRIVE_OSTYPE=$1 13 | 14 | echo " -----> Running base image script base-$WARPDRIVE_OSTYPE" 15 | 16 | exec $WARPDRIVE_SCRIPTS/base-$WARPDRIVE_OSTYPE 17 | -------------------------------------------------------------------------------- /warpdrive/etc/warpdrive-build: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ x"$WARPDRIVE_DEBUG" != x"" ]; then 4 | set -x 5 | fi 6 | 7 | # This is the script that prepares the Python application to be run. It 8 | # would normally be triggered from a derived docker image explicitly, 9 | # as a deferred ONBUILD action, or from an S2I builder. 10 | # 11 | # The main purpose of the script is to run 'pip install' on any user 12 | # supplied 'requirements.txt' file. In addition to that though, it will 13 | # also run any user provided scripts for performing actions before or 14 | # after the installation of any application dependencies. These user 15 | # scripts enable the ability to install additional system packages, or 16 | # run any application specific startup commands for preparing an 17 | 18 | # Ensure that any failure within this script or a user provided script 19 | # causes this script to fail immediately. This eliminates the need to 20 | # check individual statuses for anything which is run and prematurely 21 | # exit. Note that the feature of bash to exit in this way isn't 22 | # foolproof. Ensure that you heed any advice in: 23 | # 24 | # http://mywiki.wooledge.org/BashFAQ/105 25 | # http://fvue.nl/wiki/Bash:_Error_handling 26 | # 27 | # and use best practices to ensure that failures are always detected. 28 | # Any user supplied scripts should also use this failure mode. 29 | 30 | set -eo pipefail 31 | 32 | # Specify the build system and target. The build target is mainly for 33 | # pre build action hooks. 34 | 35 | WARPDRIVE_PHASE=build 36 | export WARPDRIVE_PHASE 37 | 38 | WARPDRIVE_BUILD_SYSTEM=${WARPDRIVE_BUILD_SYSTEM:-local} 39 | WARPDRIVE_BUILD_TARGET=application 40 | 41 | export WARPDRIVE_BUILD_SYSTEM 42 | export WARPDRIVE_BUILD_TARGET 43 | 44 | # Root directory for the runtime environment of the application. 45 | 46 | WARPDRIVE_APP_ROOT=${WARPDRIVE_APP_ROOT:-/opt/app-root} 47 | 48 | export WARPDRIVE_APP_ROOT 49 | 50 | # Root directory for the source code of the application. 51 | 52 | WARPDRIVE_SRC_ROOT=${WARPDRIVE_SRC_ROOT:-$WARPDRIVE_APP_ROOT/src} 53 | 54 | export WARPDRIVE_SRC_ROOT 55 | 56 | # Home directory of the application within the source code. 57 | 58 | if [ -f ${WARPDRIVE_SRC_ROOT}/.warpdrive/home ]; then 59 | WARPDRIVE_APP_HOME="`cat ${WARPDRIVE_SRC_ROOT}/.warpdrive/home`" 60 | fi 61 | 62 | case "$WARPDRIVE_APP_HOME" in 63 | /*) 64 | ;; 65 | "") 66 | WARPDRIVE_APP_HOME=$WARPDRIVE_SRC_ROOT 67 | ;; 68 | *) 69 | WARPDRIVE_APP_HOME=$WARPDRIVE_SRC_ROOT/$WARPDRIVE_APP_HOME 70 | echo " -----> Using application home directory $WARPDRIVE_APP_HOME." 71 | ;; 72 | esac 73 | 74 | export WARPDRIVE_APP_HOME 75 | 76 | # Create directories for temporary files and persistent data. If we 77 | # are running as non root user but in root group, assume we also need 78 | # to make the directories writable to group. 79 | 80 | mkdir -p $WARPDRIVE_APP_ROOT/tmp 81 | mkdir -p $WARPDRIVE_APP_ROOT/data 82 | 83 | WARPDRIVE_USER_ID=$(id -u) 84 | WARPDRIVE_GROUP_ID=$(id -g) 85 | 86 | if [ "$WARPDRIVE_USER_ID" != "0" -a "$WARPDRIVE_GROUP_ID" = "0" ]; then 87 | chmod g+w $WARPDRIVE_APP_ROOT/tmp 88 | chmod g+w $WARPDRIVE_APP_ROOT/data 89 | fi 90 | 91 | # Make sure we are in the top level directory of the source code for 92 | # when performing the build steps. When later starting the application 93 | # we will use the home directory for the application, but not here. 94 | 95 | cd $WARPDRIVE_SRC_ROOT 96 | 97 | # Activate the Python virtual environment if one has been created. 98 | 99 | if [ -f $WARPDRIVE_APP_ROOT/bin/activate ]; then 100 | . $WARPDRIVE_APP_ROOT/bin/activate 101 | fi 102 | 103 | # Run any user supplied script to be run prior to installing application 104 | # dependencies. This is to allow additional system packages to be 105 | # installed that may be required by any Python modules which are being 106 | # installed. The script must be executable in order to be run. It is not 107 | # possible for this script to change the permissions so it is executable 108 | # and then run it, due to some docker bug which results in the text file 109 | # being busy. For more details see: 110 | # 111 | # https://github.com/docker/docker/issues/9547 112 | 113 | if [ -f ${WARPDRIVE_SRC_ROOT}/.warpdrive/action_hooks/pre-build ]; then 114 | if [ ! -x ${WARPDRIVE_SRC_ROOT}/.warpdrive/action_hooks/pre-build ]; then 115 | echo "WARNING: Script ${WARPDRIVE_SRC_ROOT}/.warpdrive/action_hooks/pre-build not executable." 116 | fi 117 | fi 118 | 119 | if [ -x ${WARPDRIVE_SRC_ROOT}/.warpdrive/action_hooks/pre-build ]; then 120 | echo " -----> Running ${WARPDRIVE_SRC_ROOT}/.warpdrive/action_hooks/pre-build" 121 | ${WARPDRIVE_SRC_ROOT}/.warpdrive/action_hooks/pre-build 122 | fi 123 | 124 | # Now source environment variables. First evaluate common environment 125 | # variables from '.warpdrive/environment' if it exists. Then source 126 | # variables from '.warpdrive/action_hooks/build-env' if it exists. Any 127 | # variables set in either script will be automatically exported. It is 128 | # possible to use script logic, but temporary variables would need to be 129 | # cleaned up manually due to variables set being automatically exported. 130 | 131 | if [ -f $WARPDRIVE_SRC_ROOT/.warpdrive/environment ]; then 132 | set -a; . $WARPDRIVE_SRC_ROOT/.warpdrive/environment; set +a 133 | fi 134 | 135 | if [ -f $WARPDRIVE_SRC_ROOT/.warpdrive/action_hooks/build-env ]; then 136 | set -a; . $WARPDRIVE_SRC_ROOT/.warpdrive/action_hooks/build-env; set +a 137 | fi 138 | 139 | # Now run 'pip' to install any required Python packages based on the 140 | # contents of the 'requirements.txt' file. 141 | 142 | WARPDRIVE_BLD_ROOT=${WARPDRIVE_BLD_ROOT:-/tmp/warpdrive-build.$$} 143 | 144 | WARPDRIVE_PIP_OPTIONS= 145 | 146 | if [ -d ${WARPDRIVE_SRC_ROOT}/.warpdrive/wheelhouse ]; then 147 | echo " -----> Found Python wheelhouse of packages" 148 | WARPDRIVE_PIP_OPTIONS="--find-links file://${WARPDRIVE_SRC_ROOT}/.warpdrive/wheelhouse" 149 | fi 150 | 151 | if [ x"$WARPDRIVE_PIP_NO_INDEX" != x"" ]; then 152 | WARPDRIVE_PIP_OPTIONS="$WARPDRIVE_PIP_OPTIONS --no-index" 153 | fi 154 | 155 | if [ x"$WARPDRIVE_ENV_NAME" == x"" ]; then 156 | WARPDRIVE_PIP_OPTIONS="$WARPDRIVE_PIP_OPTIONS --no-cache-dir" 157 | fi 158 | 159 | WARPDRIVE_PIP_PACKAGES=${WARPDRIVE_PIP_PACKAGES:-requirements.txt} 160 | 161 | for filename in $WARPDRIVE_PIP_PACKAGES; do 162 | if [ -f $filename ]; then 163 | echo " -----> Installing dependencies with pip ($filename)" 164 | pip install $WARPDRIVE_PIP_OPTIONS --exists-action=w \ 165 | --src=$WARPDRIVE_BLD_ROOT -r $filename 166 | fi 167 | done 168 | 169 | # We also install any application package if a 'setup.py' file is 170 | # present but there is no 'requirements.txt' file. This is installed in 171 | # 'develop' mode so is linked into Python installation. This allows for 172 | # live source code updates in the container still. 173 | 174 | if [ -f setup.py -a ! -f requirements.txt ]; then 175 | echo "---> Installing application from setup.py" 176 | python setup.py develop 177 | fi 178 | 179 | # Determine the type of deployment, falling back to 'auto' if none is 180 | # defined. If 'auto', we will try and automatically determine how the 181 | # web application should be started or which WSGI server to use. 182 | 183 | if [ -f ${WARPDRIVE_SRC_ROOT}/.warpdrive/deploy_mode ]; then 184 | WARPDRIVE_DEPLOY_MODE="`cat ${WARPDRIVE_SRC_ROOT}/.warpdrive/deploy_mode`" 185 | else 186 | WARPDRIVE_DEPLOY_MODE=${WARPDRIVE_DEPLOY_MODE:-auto} 187 | fi 188 | 189 | # Determine which WSGI server should be used if hosting a WSGI application 190 | # directly, or if hosting a Django based web application. 191 | 192 | if [ -f ${WARPDRIVE_SRC_ROOT}/.warpdrive/server_type ]; then 193 | WARPDRIVE_SERVER_TYPE="`cat ${WARPDRIVE_SRC_ROOT}/.warpdrive/server_type`" 194 | else 195 | WARPDRIVE_SERVER_TYPE=${WARPDRIVE_SERVER_TYPE:-mod_wsgi} 196 | fi 197 | 198 | # See whether WSGI server is actually installed and if it isn't and we 199 | # may require it, then install it. It may already be installed for 200 | # mod_wsgi if using the Docker base image which has it pre installed. 201 | # 202 | # For WSGI servers which themselves can't handle serving up static 203 | # files, also install the Whitenoise WSGI middleware, which we will use 204 | # for hosting any static files that may need to be served as required 205 | # for certain web frameworks such as Django. 206 | 207 | if [ "$WARPDRIVE_DEPLOY_MODE" = "gunicorn" ]; then 208 | WARPDRIVE_SERVER_TYPE="gunicorn" 209 | fi 210 | 211 | if [ "$WARPDRIVE_DEPLOY_MODE" = "mod_wsgi" ]; then 212 | WARPDRIVE_SERVER_TYPE="mod_wsgi" 213 | fi 214 | 215 | if [ "$WARPDRIVE_DEPLOY_MODE" = "uwsgi" ]; then 216 | WARPDRIVE_SERVER_TYPE="uwsgi" 217 | fi 218 | 219 | if [ "$WARPDRIVE_DEPLOY_MODE" = "waitress" ]; then 220 | WARPDRIVE_SERVER_TYPE="waitress" 221 | fi 222 | 223 | if [ "$WARPDRIVE_SERVER_TYPE" = "gunicorn" ]; then 224 | if ! (python -c "import gunicorn" &>/dev/null); then 225 | pip install $WARPDRIVE_PIP_OPTIONS gunicorn 226 | fi 227 | if ! (python -c "import whitenoise" &>/dev/null); then 228 | pip install $WARPDRIVE_PIP_OPTIONS whitenoise 229 | fi 230 | fi 231 | 232 | if [ "$WARPDRIVE_SERVER_TYPE" = "mod_wsgi" ]; then 233 | if ! (python -c "import mod_wsgi" &>/dev/null); then 234 | pip install $WARPDRIVE_PIP_OPTIONS mod_wsgi 235 | fi 236 | fi 237 | 238 | if [ "$WARPDRIVE_SERVER_TYPE" = "uwsgi" ]; then 239 | if ! (which uwsgi &>/dev/null); then 240 | pip install $WARPDRIVE_PIP_OPTIONS uwsgi 241 | fi 242 | fi 243 | 244 | if [ "$WARPDRIVE_SERVER_TYPE" = "waitress" ]; then 245 | if ! (python -c "import waitress" &>/dev/null); then 246 | pip install $WARPDRIVE_PIP_OPTIONS waitress 247 | fi 248 | if ! (python -c "import whitenoise" &>/dev/null); then 249 | pip install $WARPDRIVE_PIP_OPTIONS whitenoise 250 | fi 251 | fi 252 | 253 | # Ensure that the source directory and application home directory are 254 | # in the Python module search path and that they are searched first. 255 | 256 | PYTHONPATH=$WARPDRIVE_SRC_ROOT:$PYTHONPATH 257 | 258 | if [ "$WARPDRIVE_APP_HOME" != "$WARPDRIVE_SRC_ROOT" ]; then 259 | PYTHONPATH=$WARPDRIVE_APP_HOME:$PYTHONPATH 260 | fi 261 | 262 | export PYTHONPATH 263 | 264 | # Run any user supplied script to run after installing any application 265 | # dependencies. This is to allow any application specific setup scripts 266 | # to be run. It is not possible for this script to change the 267 | # permissions so it is executable and then run it, due to some docker 268 | # bug which results in the text file being busy. For more details see: 269 | # 270 | # https://github.com/docker/docker/issues/9547 271 | 272 | if [ -x ${WARPDRIVE_SRC_ROOT}/.warpdrive/action_hooks/build ]; then 273 | echo " -----> Running ${WARPDRIVE_SRC_ROOT}/.warpdrive/action_hooks/build" 274 | ${WARPDRIVE_SRC_ROOT}/.warpdrive/action_hooks/build 275 | fi 276 | 277 | # If we are automatically detecting the server type and we find a Django 278 | # application, trigger collection of static files if possible. 279 | 280 | function django_build_settings() { 281 | WARPDRIVE_TMP_SCRIPT="/tmp/django_build_settings_$$.py" 282 | WARPDRIVE_TMP_VALUES="/tmp/django_build_settings_$$.txt" 283 | 284 | cat > $WARPDRIVE_TMP_SCRIPT << ! 285 | from django.conf import settings 286 | with open('$WARPDRIVE_TMP_VALUES', 'w') as fp: 287 | fp.write('WARPDRIVE_DJANGO_SETTINGS="%s"' % settings.SETTINGS_MODULE) 288 | ! 289 | 290 | (cat - | python $WARPDRIVE_APP_HOME/manage.py shell -i python > /dev/null) << ! 291 | import runpy 292 | _ = runpy.run_path('$WARPDRIVE_TMP_SCRIPT') 293 | ! 294 | 295 | cat $WARPDRIVE_TMP_VALUES 296 | 297 | rm $WARPDRIVE_TMP_VALUES 298 | rm $WARPDRIVE_TMP_SCRIPT* 299 | } 300 | 301 | function django_collectstatic() { 302 | eval $(django_build_settings) 303 | 304 | WARPDRIVE_TMP_MODULE="django_settings_$$" 305 | 306 | cat > $WARPDRIVE_APP_ROOT/tmp/$WARPDRIVE_TMP_MODULE.py << ! 307 | from $WARPDRIVE_DJANGO_SETTINGS import * 308 | if 'STATIC_ROOT' not in globals(): 309 | STATIC_ROOT = '$WARPDRIVE_APP_ROOT/tmp/django/static' 310 | ! 311 | 312 | mkdir -p $WARPDRIVE_APP_ROOT/tmp/django 313 | 314 | PYTHONPATH=$WARPDRIVE_APP_ROOT/tmp:$PYTHONPATH \ 315 | DJANGO_SETTINGS_MODULE=$WARPDRIVE_TMP_MODULE \ 316 | python $WARPDRIVE_APP_HOME/manage.py collectstatic --noinput 317 | 318 | rm $WARPDRIVE_APP_ROOT/tmp/$WARPDRIVE_TMP_MODULE.py* 319 | } 320 | 321 | if [ "$WARPDRIVE_DEPLOY_MODE" = "auto" -o "$WARPDRIVE_DEPLOY_MODE" = "django" ]; then 322 | if [ -f $WARPDRIVE_APP_HOME/manage.py ]; then 323 | if grep -q DJANGO_SETTINGS_MODULE $WARPDRIVE_APP_HOME/manage.py; then 324 | echo " -----> Collecting static files for Django" 325 | django_collectstatic 326 | fi 327 | fi 328 | fi 329 | 330 | # Clean up any temporary files, including the results of checking out 331 | # any source code repositories when doing a 'pip install' from a VCS. 332 | 333 | rm -rf $WARPDRIVE_BLD_ROOT 334 | -------------------------------------------------------------------------------- /warpdrive/etc/warpdrive-destroy: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | WARPDRIVE_VIRTUALENVS=${WARPDRIVE_VIRTUALENVS:-$HOME/.warpdrive} 4 | 5 | if [ "$#" = "0" ]; then 6 | echo "Usage: warpdrive destroy PROJECT" 7 | exit 1 8 | fi 9 | 10 | if [ x"$WARPDRIVE_ENV_NAME" = x"$1" ]; then 11 | echo "Cannot delete active project, deactivate it first." 12 | exit 1 13 | fi 14 | 15 | WARPDRIVE_ENV_NAME="$1" 16 | 17 | WARPDRIVE_VENV_DIR="$WARPDRIVE_VIRTUALENVS/warpdrive+$WARPDRIVE_ENV_NAME" 18 | 19 | if [ ! -d "$WARPDRIVE_VENV_DIR" ]; then 20 | echo "The warpdrive project '$WARPDRIVE_ENV_NAME' does not exist." 21 | exit 1 22 | else 23 | echo "Destroying warpdrive project '$WARPDRIVE_ENV_NAME'." 24 | 25 | rm -r $WARPDRIVE_VENV_DIR 26 | fi 27 | -------------------------------------------------------------------------------- /warpdrive/etc/warpdrive-enter: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | exec "$@" 4 | -------------------------------------------------------------------------------- /warpdrive/etc/warpdrive-env: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # This script outputs the environment for the application or shell. 4 | 5 | WARPDRIVE_PHASE=deploy 6 | export WARPDRIVE_PHASE 7 | 8 | echo "export WARPDRIVE_PHASE=$WARPDRIVE_PHASE" 9 | 10 | # Root directory for the runtime environment of the application. 11 | 12 | WARPDRIVE_APP_ROOT=${WARPDRIVE_APP_ROOT:-/opt/app-root} 13 | export WARPDRIVE_APP_ROOT 14 | 15 | echo "export WARPDRIVE_APP_ROOT=$WARPDRIVE_APP_ROOT" 16 | 17 | # Root directory for the source code of the application. 18 | 19 | WARPDRIVE_SRC_ROOT=${WARPDRIVE_SRC_ROOT:-$WARPDRIVE_APP_ROOT/src} 20 | export WARPDRIVE_SRC_ROOT 21 | 22 | echo "export WARPDRIVE_SRC_ROOT=$WARPDRIVE_SRC_ROOT" 23 | 24 | # Home directory of the application within the source code. 25 | 26 | if [ -f ${WARPDRIVE_SRC_ROOT}/.warpdrive/home ]; then 27 | WARPDRIVE_APP_HOME="`cat ${WARPDRIVE_SRC_ROOT}/.warpdrive/home`" 28 | fi 29 | 30 | case "$WARPDRIVE_APP_HOME" in 31 | /*) 32 | ;; 33 | "") 34 | WARPDRIVE_APP_HOME=$WARPDRIVE_SRC_ROOT 35 | ;; 36 | *) 37 | WARPDRIVE_APP_HOME=$WARPDRIVE_SRC_ROOT/$WARPDRIVE_APP_HOME 38 | ;; 39 | esac 40 | 41 | export WARPDRIVE_APP_HOME 42 | 43 | echo "export WARPDRIVE_APP_HOME=$WARPDRIVE_APP_HOME" 44 | 45 | # Set the default listener port for the web application server. 46 | 47 | WARPDRIVE_HTTP_PORT=${WARPDRIVE_HTTP_PORT:-8080} 48 | export WARPDRIVE_HTTP_PORT 49 | 50 | echo "export WARPDRIVE_HTTP_PORT=$WARPDRIVE_HTTP_PORT" 51 | 52 | # Override uid and gid lookup to cope with being randomly assigned IDs 53 | # using the -u option to 'docker run'. 54 | # 55 | # For now we base whether we can run this on whether /etc/passwd is writable 56 | # as non root user, or on the existence of the libnss_wrapper.so file. 57 | # This really should be gated on whether running inside of Docker which 58 | # is where it needs to be done. 59 | 60 | running_in_docker() { 61 | awk -F/ '$2 == "docker"' /proc/self/cgroup | read 62 | } 63 | 64 | WARPDRIVE_USER_ID=$(id -u) 65 | WARPDRIVE_BUILDER_ID=${WARPDRIVE_BUILDER_ID:-1001} 66 | 67 | NSS_WRAPPER_PASSWD=$WARPDRIVE_APP_ROOT/tmp/passwd 68 | NSS_WRAPPER_GROUP=/etc/group 69 | 70 | NSS_WRAPPER_LIBRARY= 71 | 72 | NSS_WRAPPER_PASSWD_UPDATE= 73 | 74 | if [ -f /usr/local/nss_wrapper/lib64/libnss_wrapper.so ]; then 75 | NSS_WRAPPER_LIBRARY=/usr/local/nss_wrapper/lib64/libnss_wrapper.so 76 | else 77 | if [ -f /lib64/libnss_wrapper.so ]; then 78 | NSS_WRAPPER_LIBRARY=/lib64/libnss_wrapper.so 79 | fi 80 | fi 81 | 82 | if [ $WARPDRIVE_USER_ID != 0 -a $WARPDRIVE_USER_ID != $WARPDRIVE_BUILDER_ID ]; then 83 | if [ ! -f $NSS_WRAPPER_PASSWD ]; then 84 | cat /etc/passwd | sed -e "s/^.*:x:$WARPDRIVE_BUILDER_ID:0:/warpfield:x:$WARPDRIVE_BUILDER_ID:0:/" > $NSS_WRAPPER_PASSWD 85 | echo "warpdrive:x:$WARPDRIVE_USER_ID:0:Warp Drive,,,:$HOME:/bin/bash" >> $NSS_WRAPPER_PASSWD 86 | NSS_WRAPPER_PASSWD_UPDATE=1 87 | fi 88 | fi 89 | 90 | if [ x"$NSS_WRAPPER_PASSWD_UPDATE" != x"" ]; then 91 | if [ -w /etc/passwd ]; then 92 | cat $NSS_WRAPPER_PASSWD > /etc/passwd 93 | else 94 | if [ "$NSS_WRAPPER_LIBRARY" != "" ]; then 95 | echo "export NSS_WRAPPER_PASSWD=$NSS_WRAPPER_PASSWD" 96 | echo "export NSS_WRAPPER_GROUP=$NSS_WRAPPER_GROUP" 97 | 98 | echo "export LD_PRELOAD=$NSS_WRAPPER_LIBRARY" 99 | fi 100 | fi 101 | fi 102 | 103 | # Ensure that the source directory and application home directory are 104 | # in the Python module search path and that they are searched first. 105 | 106 | PYTHONPATH=$WARPDRIVE_SRC_ROOT:$PYTHONPATH 107 | 108 | if [ "$WARPDRIVE_APP_HOME" != "$WARPDRIVE_SRC_ROOT" ]; then 109 | PYTHONPATH=$WARPDRIVE_APP_HOME:$PYTHONPATH 110 | fi 111 | 112 | echo "export PYTHONPATH=$PYTHONPATH" 113 | 114 | # Add the bin directory for the Python virtual environment if it exists. 115 | 116 | if [ -f $WARPDRIVE_APP_ROOT/bin/activate ]; then 117 | PATH="$WARPDRIVE_APP_ROOT/bin:$PATH" 118 | fi 119 | 120 | echo "export PATH=\"$PATH\"" 121 | 122 | # Also need to source environment variables. First evaluate common 123 | # environment variables from '.warpdrive/environment' if it exists. Then 124 | # source variables from '.warpdrive/action_hooks/deploy-env' if it 125 | # exists. Any variables set in either script will be automatically 126 | # exported. It is possible to use script logic, but temporary variables 127 | # would need to be cleaned up manually due to variables set being 128 | # automatically exported. 129 | 130 | if [ -f $WARPDRIVE_SRC_ROOT/.warpdrive/environment ]; then 131 | cat $WARPDRIVE_SRC_ROOT/.warpdrive/environment 132 | fi 133 | 134 | if [ -f $WARPDRIVE_SRC_ROOT/.warpdrive/action_hooks/deploy-env ]; then 135 | cat $WARPDRIVE_SRC_ROOT/.warpdrive/action_hooks/deploy-env 136 | fi 137 | 138 | # Output how environment variables should be incorporated. 139 | 140 | echo '# Run this command to configure your shell:' 141 | echo '# eval "$(warpdrive env)"' 142 | -------------------------------------------------------------------------------- /warpdrive/etc/warpdrive-exec: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # This script will execute the command passed as arguments. 4 | 5 | # Setup the environment if not already done. 6 | 7 | if [ x"$WARPDRIVE_ACTION" = x"" ]; then 8 | eval "$(warpdrive env)" 9 | fi 10 | 11 | WARPDRIVE_ACTION=exec 12 | export WARPDRIVE_ACTION 13 | 14 | WARPDRIVE_PROGRAM=$1 15 | 16 | shift 17 | 18 | # If running as process ID 1 and flagged that we should run as process 19 | # ID 1, then simply exec the program. If not process ID 1, then always 20 | # exec the program. 21 | 22 | if [ $$ = 1 ]; then 23 | if [ x"$WARPDRIVE_RUN_AS_PID1" != x"" ]; then 24 | exec $WARPDRIVE_PROGRAM "$@" 25 | fi 26 | else 27 | exec $WARPDRIVE_PROGRAM "$@" 28 | fi 29 | 30 | # Should only get here if running as process ID 1, but we don't want the 31 | # program running as PID 1. In this case, if have 'tini', use it to 32 | # manage the process, else try and manage from this script with some 33 | # signal handling tricks so signals still propagated. We need to do this 34 | # as can't rely on the program handling reaping of zombie processes 35 | # properly. Note though that if fall through to backgrounding process, 36 | # the program being run better not be wanting to read standard input 37 | # as putting it in background will prevent that. 38 | 39 | if ( which tini > /dev/null 2>&1 ); then 40 | exec tini -- $WARPDRIVE_PROGRAM "$@" 41 | fi 42 | 43 | trap 'kill -TERM $PID' TERM INT 44 | 45 | $WARPDRIVE_PROGRAM "$@" & 46 | 47 | PID=$! 48 | wait $PID 49 | trap - TERM INT 50 | wait $PID 51 | STATUS=$? 52 | exit $STATUS 53 | -------------------------------------------------------------------------------- /warpdrive/etc/warpdrive-fixup: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Allow this script to fail without failing a build. 4 | 5 | set +e 6 | 7 | # Fix permissions on the given directory or file to allow group 8 | # read/write of regular files and execute of directories. 9 | 10 | [ $(id -u) -ne 0 ] && CHECK_OWNER=" -uid $(id -u)" 11 | 12 | # If argument does not exist, script will still exit with 0, but at 13 | # least we'll see something went wrong in the log 14 | 15 | if ! [ -e "$1" ] ; then 16 | echo "ERROR: File or directory $1 does not exist." >&2 17 | # We still want to end successfully. 18 | exit 0 19 | fi 20 | 21 | find -L "$1" ${CHECK_OWNER} \! -gid 0 -exec chgrp 0 {} + 22 | find -L "$1" ${CHECK_OWNER} \! -perm -g+rw -exec chmod g+rw {} + 23 | find -L "$1" ${CHECK_OWNER} -perm /u+x -a \! -perm /g+x -exec chmod g+x {} + 24 | find -L "$1" ${CHECK_OWNER} -type d \! -perm /g+x -exec chmod g+x {} + 25 | 26 | # Always end successfully. 27 | 28 | exit 0 29 | -------------------------------------------------------------------------------- /warpdrive/etc/warpdrive-help: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cat << EOF 4 | Warpdrive Client 5 | 6 | This client helps you build and deploy Python web applications, including 7 | the ability to create container images which bundle your application for 8 | deployment in container platforms such as Docker, Kubernetes and OpenShift. 9 | It also includes the capability to manage the Python virtual environment 10 | for holding packages required by your Python application. 11 | 12 | Virtual Environment Commands: 13 | activate Create and/or configure the Python virtual environment. 14 | deactivate Deactivate the current Python virtual environment. 15 | destroy Deletes the specified Python virtual environment. 16 | project Activate the specified Python virtual environment. 17 | 18 | Application Build Commands: 19 | build Run build steps to ready the application for deployment. 20 | fixup Fixup filesystem permissions when building a container image. 21 | image Create a container image which bundles the application. 22 | verify Run verification steps to check a build was successful. 23 | wheels Create a container image containing required Python wheels. 24 | 25 | Application Runtime Commands: 26 | alive Run liveness checks to verify the application is running. 27 | env Output environment settings for the application and/or shell. 28 | exec Execute a command after applying the environment settings. 29 | migrate Run any required steps to perform database migrations. 30 | ready Run readiness checks to verify application can accept requests. 31 | setup Run any required initial steps to setup the application. 32 | shell Execute a shell after applying the environment settings. 33 | start Start the application after applying the environment settings. 34 | 35 | Miscellaneous Commands: 36 | profile Output shell commands to include in login script files. 37 | rcfile Output path to shell script to be source'd in login scripts. 38 | EOF 39 | -------------------------------------------------------------------------------- /warpdrive/etc/warpdrive-image: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | PYTHON_VERSION=`python -V 2>&1 | sed -e 's/Python //' -e 's/\.[0-9]*$//' -e 's/\.//'` 4 | 5 | WARPDRIVE_BUILDER_IMAGE=${WARPDRIVE_BUILDER_IMAGE:-getwarped/warp0-debian10-python$PYTHON_VERSION} 6 | 7 | if [ -f $WARPDRIVE_SRC_ROOT/.warpdrive/builder_image ]; then 8 | WARPDRIVE_BUILDER_IMAGE=`cat $WARPDRIVE_SRC_ROOT/.warpdrive/builder_image` 9 | fi 10 | 11 | WARPDRIVE_IMAGE_NAME= 12 | WARPDRIVE_BUILD_TARGET=application 13 | WARPDRIVE_WHEELHOUSE= 14 | WARPDRIVE_PIP_NO_INDEX= 15 | 16 | while [ "$#" != "0" ]; do 17 | case "$1" in 18 | --builder-image=*) 19 | WARPDRIVE_BUILDER_IMAGE=`echo $1 | sed -e 's/--builder=//'` 20 | ;; 21 | --builder-image) 22 | WARPDRIVE_BUILDER_IMAGE=$2 23 | shift 24 | ;; 25 | --build-target=*) 26 | WARPDRIVE_BUILD_TARGET=`echo $1 | sed -e 's/--build-target=//'` 27 | ;; 28 | --build-target) 29 | WARPDRIVE_BUILD_TARGET=$2 30 | shift 31 | ;; 32 | --wheelhouse=*) 33 | WARPDRIVE_WHEELHOUSE=`echo $1 | sed -e 's/--wheelhouse=//'` 34 | ;; 35 | --wheelhouse) 36 | WARPDRIVE_WHEELHOUSE=$2 37 | shift 38 | ;; 39 | --no-index) 40 | WARPDRIVE_PIP_NO_INDEX=1 41 | ;; 42 | *) 43 | WARPDRIVE_IMAGE_NAME=$1 44 | ;; 45 | esac 46 | 47 | shift 48 | done 49 | 50 | WARPDRIVE_TMP_FILES=/tmp/warpdrive-image.$$ 51 | 52 | mkdir -p /tmp/warpdrive-image.$$ 53 | 54 | function cleanup() { 55 | rm -rf $WARPDRIVE_TMP_FILES 56 | docker rm warpdrive-image-$$ 57 | } 58 | 59 | trap "cleanup; exit" SIGHUP SIGINT SIGTERM 60 | 61 | tar --exclude .git -c -C $WARPDRIVE_SRC_ROOT -f - . | \ 62 | tar xCf $WARPDRIVE_TMP_FILES - 63 | 64 | if [ "$WARPDRIVE_WHEELHOUSE" != "" ]; then 65 | docker run --name warpdrive-image-$$ $WARPDRIVE_WHEELHOUSE true 66 | 67 | docker cp warpdrive-image-$$:/opt/app-root/src/.warpdrive - | \ 68 | tar xCf $WARPDRIVE_TMP_FILES - 69 | 70 | docker rm warpdrive-image-$$ 71 | fi 72 | 73 | WARPDRIVE_S2I_LOGLEVEL=${WARPDRIVE_S2I_LOGLEVEL:-0} 74 | 75 | if [ "$WARPDRIVE_BUILD_TARGET" = "wheelhouse" ]; then 76 | if [ "$WARPDRIVE_IMAGE_NAME" = "" ]; then 77 | WARPDRIVE_IMAGE_NAME=warpdrive-$WARPDRIVE_ENV_NAME-wheelhouse 78 | fi 79 | 80 | cat > $WARPDRIVE_TMP_FILES/Dockerfile-warpdrive << EOF 81 | FROM $WARPDRIVE_BUILDER_IMAGE 82 | 83 | COPY --chown=1001:0 . /opt/app-root/src/ 84 | 85 | RUN . /opt/app-root/etc/profile && warpdrive wheels 86 | EOF 87 | 88 | docker build -f $WARPDRIVE_TMP_FILES/Dockerfile-warpdrive \ 89 | -t $WARPDRIVE_IMAGE_NAME $WARPDRIVE_TMP_FILES 90 | 91 | STATUS=$? 92 | else 93 | if [ "$WARPDRIVE_IMAGE_NAME" = "" ]; then 94 | WARPDRIVE_IMAGE_NAME=warpdrive-$WARPDRIVE_ENV_NAME-application 95 | fi 96 | 97 | cat > $WARPDRIVE_TMP_FILES/Dockerfile-warpdrive << EOF 98 | FROM $WARPDRIVE_BUILDER_IMAGE 99 | 100 | COPY --chown=1001:0 . /opt/app-root/src/ 101 | 102 | RUN assemble-image 103 | 104 | CMD [ "start-container" ] 105 | 106 | EXPOSE 8080 107 | EOF 108 | 109 | docker build -f $WARPDRIVE_TMP_FILES/Dockerfile-warpdrive \ 110 | -t $WARPDRIVE_IMAGE_NAME $WARPDRIVE_TMP_FILES 111 | 112 | STATUS=$? 113 | fi 114 | 115 | rm -rf $WARPDRIVE_TMP_FILES 116 | 117 | exit $STATUS 118 | -------------------------------------------------------------------------------- /warpdrive/etc/warpdrive-migrate: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ x"$WARPDRIVE_DEBUG" != x"" ]; then 4 | set -x 5 | fi 6 | 7 | # Ensure that any failure within this script or a user provided script 8 | # causes this script to fail immediately. This eliminates the need to 9 | # check individual statuses for anything which is run and prematurely 10 | # exit. Note that the feature of bash to exit in this way isn't 11 | # foolproof. Ensure that you heed any advice in: 12 | # 13 | # http://mywiki.wooledge.org/BashFAQ/105 14 | # http://fvue.nl/wiki/Bash:_Error_handling 15 | # 16 | # and use best practices to ensure that failures are always detected. 17 | # Any user supplied scripts should also use this failure mode. 18 | 19 | set -eo pipefail 20 | 21 | # Setup the environment if not already done. 22 | 23 | if [ x"$WARPDRIVE_ACTION" = x"" ]; then 24 | eval "$(warpdrive env)" 25 | fi 26 | 27 | WARPDRIVE_ACTION=migrate 28 | export WARPDRIVE_ACTION 29 | 30 | # Make sure we are in the correct working directory for the application. 31 | 32 | cd $WARPDRIVE_SRC_ROOT 33 | 34 | # Run any user supplied script to be run to update configuration files 35 | # based on environment variables available at deployment time. 36 | 37 | if [ ! -f $WARPDRIVE_APP_ROOT/markers/deploy-cfg ]; then 38 | # Create the marker file so we know if we have been run already. 39 | 40 | mkdir -p $WARPDRIVE_APP_ROOT/markers 41 | date > $WARPDRIVE_APP_ROOT/markers/deploy-cfg 42 | 43 | if [ -f $WARPDRIVE_SRC_ROOT/.warpdrive/action_hooks/deploy-cfg ]; then 44 | if [ ! -x $WARPDRIVE_SRC_ROOT/.warpdrive/action_hooks/deploy-cfg ]; then 45 | echo "# WARNING: Script $WARPDRIVE_SRC_ROOT/.warpdrive/action_hooks/deploy-cfg not executable." 46 | fi 47 | fi 48 | 49 | if [ -x $WARPDRIVE_SRC_ROOT/.warpdrive/action_hooks/deploy-cfg ]; then 50 | echo "# INFO: Running script $WARPDRIVE_SRC_ROOT/.warpdrive/action_hooks/deploy-cfg" 51 | $WARPDRIVE_SRC_ROOT/.warpdrive/action_hooks/deploy-cfg 52 | fi 53 | fi 54 | 55 | # Run any user supplied script to perform data migration between 56 | # deployments of the application. The script must be executable in order 57 | # to be run. It is not possible for this script to change the 58 | # permissions so it is executable and then run it, due to some docker 59 | # bug which results in the text file being busy. For more details see: 60 | # 61 | # https://github.com/docker/docker/issues/9547 62 | 63 | if [ -f .warpdrive/action_hooks/migrate ]; then 64 | if [ ! -x .warpdrive/action_hooks/migrate ]; then 65 | echo "WARNING: Script .warpdrive/action_hooks/migrate not executable." 66 | fi 67 | fi 68 | 69 | if [ -x .warpdrive/action_hooks/migrate ]; then 70 | echo " -----> Running .warpdrive/action_hooks/migrate" 71 | exec .warpdrive/action_hooks/migrate 72 | fi 73 | -------------------------------------------------------------------------------- /warpdrive/etc/warpdrive-profile: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ x"$WARPDRIVE_DEBUG" != x"" ]; then 4 | set -x 5 | fi 6 | 7 | # This script will output shell commands to be added to the shell login 8 | # profile scripts to enable full access to warpdrive commands, including 9 | # those for managing Python virtual environments. 10 | 11 | if [ x"$WARPDRIVE_MASTER" != x"" ]; then 12 | WARPDRIVE=$WARPDRIVE_MASTER 13 | fi 14 | 15 | if [ x"$WARPDRIVE" = x"" ]; then 16 | WARPDRIVE=`which warpdrive` 17 | fi 18 | 19 | cat << EOF 20 | WARPDRIVE=$WARPDRIVE 21 | export WARPDRIVE 22 | 23 | . \`\$WARPDRIVE rcfile\` 24 | EOF 25 | -------------------------------------------------------------------------------- /warpdrive/etc/warpdrive-rcfile: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ x"$WARPDRIVE_DEBUG" != x"" ]; then 4 | set -x 5 | fi 6 | 7 | # This script will output the name of a rcfile to be source'd as 8 | # part of login shell scripts. 9 | 10 | WARPDRIVE_RCFILE=`which $0` 11 | WARPDRIVE_SCRIPTS=`dirname $WARPDRIVE_RCFILE` 12 | 13 | echo "$WARPDRIVE_SCRIPTS/shell-rcfile" 14 | -------------------------------------------------------------------------------- /warpdrive/etc/warpdrive-ready: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ x"$WARPDRIVE_DEBUG" != x"" ]; then 4 | set -x 5 | fi 6 | 7 | # Ensure that any failure within this script or a user provided script 8 | # causes this script to fail immediately. This eliminates the need to 9 | # check individual statuses for anything which is run and prematurely 10 | # exit. Note that the feature of bash to exit in this way isn't 11 | # foolproof. Ensure that you heed any advice in: 12 | # 13 | # http://mywiki.wooledge.org/BashFAQ/105 14 | # http://fvue.nl/wiki/Bash:_Error_handling 15 | # 16 | # and use best practices to ensure that failures are always detected. 17 | # Any user supplied scripts should also use this failure mode. 18 | 19 | set -eo pipefail 20 | 21 | # Setup the environment if not already done. 22 | 23 | if [ x"$WARPDRIVE_ACTION" = x"" ]; then 24 | eval "$(warpdrive env)" 25 | fi 26 | 27 | WARPDRIVE_ACTION=ready 28 | export WARPDRIVE_ACTION 29 | 30 | # Make sure we are in the correct working directory for the application. 31 | 32 | cd $WARPDRIVE_SRC_ROOT 33 | 34 | # Run any user supplied script to test whether the deployed application 35 | # is ready to handle accepting requests. The script must be executable 36 | # in order to be run. It is not possible for this script to change the 37 | # permissions so it is executable and then run it, due to some docker 38 | # bug which results in the text file being busy. For more details see: 39 | # 40 | # https://github.com/docker/docker/issues/9547 41 | 42 | if [ -f .warpdrive/action_hooks/ready ]; then 43 | if [ ! -x .warpdrive/action_hooks/ready ]; then 44 | echo "WARNING: Script .warpdrive/action_hooks/ready not executable." 45 | fi 46 | fi 47 | 48 | if [ -x .warpdrive/action_hooks/ready ]; then 49 | echo " -----> Running .warpdrive/action_hooks/ready" 50 | exec .warpdrive/action_hooks/ready 51 | fi 52 | -------------------------------------------------------------------------------- /warpdrive/etc/warpdrive-setup: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ x"$WARPDRIVE_DEBUG" != x"" ]; then 4 | set -x 5 | fi 6 | 7 | # Ensure that any failure within this script or a user provided script 8 | # causes this script to fail immediately. This eliminates the need to 9 | # check individual statuses for anything which is run and prematurely 10 | # exit. Note that the feature of bash to exit in this way isn't 11 | # foolproof. Ensure that you heed any advice in: 12 | # 13 | # http://mywiki.wooledge.org/BashFAQ/105 14 | # http://fvue.nl/wiki/Bash:_Error_handling 15 | # 16 | # and use best practices to ensure that failures are always detected. 17 | # Any user supplied scripts should also use this failure mode. 18 | 19 | set -eo pipefail 20 | 21 | # Setup the environment if not already done. 22 | 23 | if [ x"$WARPDRIVE_ACTION" = x"" ]; then 24 | eval "$(warpdrive env)" 25 | fi 26 | 27 | WARPDRIVE_ACTION=setup 28 | export WARPDRIVE_ACTION 29 | 30 | # Make sure we are in the correct working directory for the application. 31 | 32 | cd $WARPDRIVE_SRC_ROOT 33 | 34 | # Run any user supplied script to be run to update configuration files 35 | # based on environment variables available at deployment time. 36 | 37 | if [ ! -f $WARPDRIVE_APP_ROOT/markers/deploy-cfg ]; then 38 | # Create the marker file so we know if we have been run already. 39 | 40 | mkdir -p $WARPDRIVE_APP_ROOT/markers 41 | date > $WARPDRIVE_APP_ROOT/markers/deploy-cfg 42 | 43 | if [ -f $WARPDRIVE_SRC_ROOT/.warpdrive/action_hooks/deploy-cfg ]; then 44 | if [ ! -x $WARPDRIVE_SRC_ROOT/.warpdrive/action_hooks/deploy-cfg ]; then 45 | echo "# WARNING: Script $WARPDRIVE_SRC_ROOT/.warpdrive/action_hooks/deploy-cfg not executable." 46 | fi 47 | fi 48 | 49 | if [ -x $WARPDRIVE_SRC_ROOT/.warpdrive/action_hooks/deploy-cfg ]; then 50 | echo "# INFO: Running script $WARPDRIVE_SRC_ROOT/.warpdrive/action_hooks/deploy-cfg" 51 | $WARPDRIVE_SRC_ROOT/.warpdrive/action_hooks/deploy-cfg 52 | fi 53 | fi 54 | 55 | # Run any user supplied script to perform any initial database setup or 56 | # application setup. The script must be executable in order to be run. 57 | # It is not possible for this script to change the permissions so it is 58 | # executable and then run it, due to some docker bug which results in 59 | # the text file being busy. For more details see: 60 | # 61 | # https://github.com/docker/docker/issues/9547 62 | 63 | if [ -f .warpdrive/action_hooks/setup ]; then 64 | if [ ! -x .warpdrive/action_hooks/setup ]; then 65 | echo "WARNING: Script .warpdrive/action_hooks/setup not executable." 66 | fi 67 | fi 68 | 69 | if [ -x .warpdrive/action_hooks/setup ]; then 70 | echo " -----> Running .warpdrive/action_hooks/setup" 71 | exec .warpdrive/action_hooks/setup 72 | fi 73 | -------------------------------------------------------------------------------- /warpdrive/etc/warpdrive-shell: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # This script will run an interactive bash shell. 4 | 5 | # Setup the environment if not already done. 6 | 7 | if [ x"$WARPDRIVE_ACTION" = x"" ]; then 8 | eval "$(warpdrive env)" 9 | fi 10 | 11 | WARPDRIVE_ACTION=shell 12 | export WARPDRIVE_ACTION 13 | 14 | # Now finally run bash. 15 | 16 | exec bash 17 | -------------------------------------------------------------------------------- /warpdrive/etc/warpdrive-start: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ x"$WARPDRIVE_DEBUG" != x"" ]; then 4 | set -x 5 | fi 6 | 7 | # This script will run the Python web server. 8 | 9 | # Setup the environment if not already done. 10 | 11 | if [ x"$WARPDRIVE_ACTION" = x"" ]; then 12 | eval "$(warpdrive env)" 13 | fi 14 | 15 | WARPDRIVE_ACTION=start 16 | export WARPDRIVE_ACTION 17 | 18 | # Make sure we are in the top level directory of the source code for 19 | # when performing the deploy steps. When later starting the application 20 | # we will use the home directory for the application, but not here. 21 | 22 | cd $WARPDRIVE_SRC_ROOT 23 | 24 | # Run any user supplied script to be run to update configuration files 25 | # based on environment variables available at deployment time. 26 | 27 | if [ ! -f $WARPDRIVE_APP_ROOT/markers/deploy-cfg ]; then 28 | # Create the marker file so we know if we have been run already. 29 | 30 | mkdir -p $WARPDRIVE_APP_ROOT/markers 31 | date > $WARPDRIVE_APP_ROOT/markers/deploy-cfg 32 | 33 | if [ -f $WARPDRIVE_SRC_ROOT/.warpdrive/action_hooks/deploy-cfg ]; then 34 | if [ ! -x $WARPDRIVE_SRC_ROOT/.warpdrive/action_hooks/deploy-cfg ]; then 35 | echo "# WARNING: Script $WARPDRIVE_SRC_ROOT/.warpdrive/action_hooks/deploy-cfg not executable." 36 | fi 37 | fi 38 | 39 | if [ -x $WARPDRIVE_SRC_ROOT/.warpdrive/action_hooks/deploy-cfg ]; then 40 | echo "# INFO: Running script $WARPDRIVE_SRC_ROOT/.warpdrive/action_hooks/deploy-cfg" 41 | $WARPDRIVE_SRC_ROOT/.warpdrive/action_hooks/deploy-cfg 42 | fi 43 | fi 44 | 45 | # Run any user supplied script to be run prior to starting the 46 | # application in the actual container. The script must be executable in 47 | # order to be run. It is not possible for this script to change the 48 | # permissions so it is executable and then run it, due to some docker 49 | # bug which results in the text file being busy. For more details see: 50 | # 51 | # https://github.com/docker/docker/issues/9547 52 | 53 | if [ -f $WARPDRIVE_SRC_ROOT/.warpdrive/action_hooks/deploy ]; then 54 | if [ ! -x $WARPDRIVE_SRC_ROOT/.warpdrive/action_hooks/deploy ]; then 55 | echo "WARNING: Script $WARPDRIVE_SRC_ROOT/.warpdrive/action_hooks/deploy not executable." 56 | fi 57 | fi 58 | 59 | if [ -x $WARPDRIVE_SRC_ROOT/.warpdrive/action_hooks/deploy ]; then 60 | echo " -----> Running $WARPDRIVE_SRC_ROOT/.warpdrive/action_hooks/deploy" 61 | $WARPDRIVE_SRC_ROOT/.warpdrive/action_hooks/deploy 62 | fi 63 | 64 | # Now change directory to the application home directory. All checks are 65 | # from this point done relative to this directory. 66 | 67 | cd $WARPDRIVE_APP_HOME 68 | 69 | # Determine the type of deployment, falling back to 'auto' if none is 70 | # defined. If 'auto', we will try and automatically determine how the 71 | # web application should be started or which WSGI server to use. 72 | 73 | if [ -f ${WARPDRIVE_SRC_ROOT}/.warpdrive/deploy_mode ]; then 74 | WARPDRIVE_DEPLOY_MODE="`cat ${WARPDRIVE_SRC_ROOT}/.warpdrive/deploy_mode`" 75 | else 76 | WARPDRIVE_DEPLOY_MODE=${WARPDRIVE_DEPLOY_MODE:-auto} 77 | fi 78 | 79 | # Determine which WSGI server should be used if hosting a WSGI application 80 | # directly, or if hosting a Django based web application. 81 | 82 | if [ -f ${WARPDRIVE_SRC_ROOT}/.warpdrive/server_type ]; then 83 | WARPDRIVE_SERVER_TYPE="`cat ${WARPDRIVE_SRC_ROOT}/.warpdrive/server_type`" 84 | else 85 | WARPDRIVE_SERVER_TYPE=${WARPDRIVE_SERVER_TYPE:-mod_wsgi} 86 | fi 87 | 88 | echo " -----> Configuring for deployment mode: of '$WARPDRIVE_DEPLOY_MODE'" 89 | echo " -----> Default WSGI server type is '$WARPDRIVE_SERVER_TYPE'" 90 | 91 | # Setup default names of files to check for different server types. 92 | 93 | WARPDRIVE_SHELL_FILE="${WARPDRIVE_SHELL_FILE:-app.sh}" 94 | WARPDRIVE_PYTHON_FILE="${WARPDRIVE_PYTHON_FILE:-app.py}" 95 | 96 | WARPDRIVE_WSGI_MODULE_NAME="${WARPDRIVE_WSGI_MODULE_NAME:-wsgi}" 97 | WARPDRIVE_WSGI_CALLABLE_OBJECT=${WARPDRIVE_WSGI_CALLABLE_OBJECT:-application} 98 | WARPDRIVE_WSGI_STATIC_URL=${WARPDRIVE_WSGI_STATIC_URL:-} 99 | WARPDRIVE_WSGI_STATIC_ROOT=${WARPDRIVE_WSGI_STATIC_ROOT:-} 100 | 101 | # Ensure that path to a shell script file uses absolute path else we 102 | # will not be able to execute it later as isn't going to be in PATH. 103 | 104 | case "$WARPDRIVE_SHELL_FILE" in 105 | /*) 106 | ;; 107 | *) 108 | WARPDRIVE_SHELL_FILE="$WARPDRIVE_SRC_ROOT/$WARPDRIVE_SHELL_FILE" 109 | ;; 110 | esac 111 | 112 | # Utility functions for deducing server args for various cases. 113 | 114 | function wsgi_module_path() { 115 | WARPDRIVE_WSGI_MODULE_FILE="`echo $WARPDRIVE_WSGI_MODULE_NAME | \ 116 | sed -e 's%\.%/%g'`.py" 117 | WARPDRIVE_WSGI_PACKAGE_FILE="`echo $WARPDRIVE_WSGI_MODULE_NAME | \ 118 | sed -e 's%\.%/%g'`/__init__.py" 119 | 120 | WARPDRIVE_WSGI_FILE="" 121 | 122 | if [ -f $WARPDRIVE_WSGI_MODULE_FILE ]; then 123 | WARPDRIVE_WSGI_FILE=$WARPDRIVE_WSGI_MODULE_FILE 124 | fi 125 | 126 | if [ -f $WARPDRIVE_WSGI_PACKAGE_FILE ]; then 127 | WARPDRIVE_WSGI_FILE=$WARPDRIVE_WSGI_PACKAGE_FILE 128 | fi 129 | 130 | echo $WARPDRIVE_WSGI_FILE 131 | } 132 | 133 | function django_start_settings() { 134 | WARPDRIVE_TMP_SCRIPT="/tmp/django_start_settings_$$.py" 135 | WARPDRIVE_TMP_VALUES="/tmp/django_start_settings_$$.txt" 136 | 137 | cat > $WARPDRIVE_TMP_SCRIPT << ! 138 | from django.conf import settings 139 | with open('$WARPDRIVE_TMP_VALUES', 'w') as fp: 140 | settings_module = getattr(settings, 'SETTINGS_MODULE') 141 | 142 | wsgi_application = getattr(settings, 'WSGI_APPLICATION') 143 | 144 | entry_point = '.'.join(wsgi_application.split('.')[:-1]) 145 | callable_object = wsgi_application.split('.')[-1] 146 | 147 | static_url = getattr(settings, 'STATIC_URL') 148 | 149 | if not static_url: 150 | static_url = '/static/' 151 | 152 | static_root = getattr(settings, 'STATIC_ROOT') 153 | 154 | if not static_root: 155 | static_root = '$WARPDRIVE_APP_ROOT/tmp/django/static' 156 | 157 | variables = [ 158 | 'DJANGO_SETTINGS_MODULE="%s"' % settings_module, 159 | 'WARPDRIVE_WSGI_MODULE_NAME=%s' % entry_point, 160 | 'WARPDRIVE_WSGI_CALLABLE_OBJECT=%s' % callable_object] 161 | 162 | if static_url.endswith('/') and not static_root.endswith('/'): 163 | static_root = static_root + '/' 164 | 165 | variables.append('WARPDRIVE_WSGI_STATIC_URL=%s' % static_url) 166 | variables.append('WARPDRIVE_WSGI_STATIC_ROOT=%s' % static_root) 167 | 168 | fp.write('\n'.join(variables)) 169 | ! 170 | 171 | (cat - | python manage.py shell -i python > /dev/null) << ! 172 | import runpy 173 | _ = runpy.run_path('$WARPDRIVE_TMP_SCRIPT') 174 | ! 175 | 176 | cat $WARPDRIVE_TMP_VALUES 177 | 178 | rm $WARPDRIVE_TMP_VALUES 179 | rm $WARPDRIVE_TMP_SCRIPT* 180 | } 181 | 182 | function server_args() { 183 | WARPDRIVE_SERVER_ARGS="$WARPDRIVE_SERVER_ARGS $*" 184 | } 185 | 186 | function create_whitenoise_wrapper() { 187 | mkdir -p $WARPDRIVE_APP_ROOT/tmp/modules 188 | cat > $WARPDRIVE_APP_ROOT/tmp/modules/whitenoise_wrapper.py << ! 189 | from whitenoise import WhiteNoise 190 | 191 | from $WARPDRIVE_WSGI_MODULE_NAME import $WARPDRIVE_WSGI_CALLABLE_OBJECT as _application 192 | 193 | application = WhiteNoise(_application) 194 | application.add_files('$WARPDRIVE_WSGI_STATIC_ROOT', prefix='$WARPDRIVE_WSGI_STATIC_URL') 195 | ! 196 | } 197 | 198 | # Now check whether server type of 'shell' is selected. 199 | 200 | if [ "$WARPDRIVE_DEPLOY_MODE" = "shell" ]; then 201 | server_args $WARPDRIVE_SHELL_FILE 202 | fi 203 | 204 | # Now check whether server of 'python' is selected. 205 | 206 | if [ "$WARPDRIVE_DEPLOY_MODE" = "python" ]; then 207 | server_args $WARPDRIVE_PYTHON_FILE 208 | fi 209 | 210 | # Now check whether server of 'wsgi' is selected. 211 | 212 | if [ "$WARPDRIVE_DEPLOY_MODE" = "wsgi" ]; then 213 | if [ "$(wsgi_module_path)" != "" ]; then 214 | # WARPDRIVE_DEPLOY_MODE="wsgi" 215 | true 216 | fi 217 | fi 218 | 219 | # Now check whether server of 'django' is selected. 220 | 221 | if [ "$WARPDRIVE_DEPLOY_MODE" = "django" -a -f manage.py ]; then 222 | if grep -q DJANGO_SETTINGS_MODULE manage.py; then 223 | WARPDRIVE_DEPLOY_MODE="wsgi" 224 | eval "$(django_start_settings)" 225 | export DJANGO_SETTINGS_MODULE 226 | fi 227 | fi 228 | 229 | # If instead we are in automatic mode, we are going to try a number of 230 | # different strategies. First up we are going to check whether there 231 | # exists an executable 'app.sh' program in the top level directory of 232 | # the application. If there is, we switch to 'shell' server type. 233 | 234 | if [ "$WARPDRIVE_DEPLOY_MODE" = "auto" -a -x $WARPDRIVE_SHELL_FILE ]; then 235 | WARPDRIVE_DEPLOY_MODE="shell" 236 | WARPDRIVE_SERVER_ARGS="$WARPDRIVE_SHELL_FILE" 237 | fi 238 | 239 | # Now check whether there exists an 'app.py' script file in the top 240 | # level directory. If there is, we switch to 'python' server type. 241 | 242 | if [ "$WARPDRIVE_DEPLOY_MODE" = "auto" -a -f $WARPDRIVE_PYTHON_FILE ]; then 243 | WARPDRIVE_DEPLOY_MODE="python" 244 | WARPDRIVE_SERVER_ARGS="$WARPDRIVE_PYTHON_FILE" 245 | fi 246 | 247 | # Now check whether there exists a 'manage.py' file in the top level 248 | # directory. This can indicate we are running Django. Validate that it 249 | # is probably Django and then use 'manage.py' to determine the WSGI 250 | # application entry point, static files location and mount point. 251 | 252 | if [ "$WARPDRIVE_DEPLOY_MODE" = "auto" -a -f manage.py ]; then 253 | if grep -q DJANGO_SETTINGS_MODULE manage.py; then 254 | WARPDRIVE_DEPLOY_MODE="wsgi" 255 | eval $(django_start_settings) 256 | export DJANGO_SETTINGS_MODULE 257 | fi 258 | fi 259 | 260 | # Now check whether there exists a 'wsgi.py' file in the top level 261 | # directory. 262 | 263 | if [ "$WARPDRIVE_DEPLOY_MODE" = "auto" ]; then 264 | if [ "$(wsgi_module_path)" != "" ]; then 265 | WARPDRIVE_DEPLOY_MODE="wsgi" 266 | fi 267 | fi 268 | 269 | # Setup server args when WSGI server is to be used. 270 | 271 | if [ "$WARPDRIVE_DEPLOY_MODE" = "wsgi" ]; then 272 | if [ "$WARPDRIVE_SERVER_TYPE" = "gunicorn" ]; then 273 | WARPDRIVE_WSGI_APPLICATION="$WARPDRIVE_WSGI_MODULE_NAME:$WARPDRIVE_WSGI_CALLABLE_OBJECT" 274 | 275 | if [ "$WARPDRIVE_WSGI_STATIC_URL" != "" ]; then 276 | if [ "$WARPDRIVE_WSGI_STATIC_ROOT" != "" ]; then 277 | create_whitenoise_wrapper 278 | WARPDRIVE_WSGI_APPLICATION="whitenoise_wrapper:application" 279 | PYTHONPATH="$PYTHONPATH:$WARPDRIVE_APP_ROOT/tmp/modules" 280 | export PYTHONPATH 281 | fi 282 | fi 283 | 284 | server_args $WARPDRIVE_WSGI_APPLICATION 285 | fi 286 | 287 | if [ "$WARPDRIVE_SERVER_TYPE" = "mod_wsgi" ]; then 288 | server_args --application-type module 289 | server_args --entry-point $WARPDRIVE_WSGI_MODULE_NAME 290 | server_args --callable-object $WARPDRIVE_WSGI_CALLABLE_OBJECT 291 | 292 | if [ "$WARPDRIVE_WSGI_STATIC_URL" != "" ]; then 293 | if [ "$WARPDRIVE_WSGI_STATIC_ROOT" != "" ]; then 294 | server_args --url-alias $WARPDRIVE_WSGI_STATIC_URL $WARPDRIVE_WSGI_STATIC_ROOT 295 | fi 296 | fi 297 | fi 298 | 299 | if [ "$WARPDRIVE_SERVER_TYPE" = "uwsgi" ]; then 300 | server_args --module $WARPDRIVE_WSGI_MODULE_NAME 301 | server_args --callable $WARPDRIVE_WSGI_CALLABLE_OBJECT 302 | 303 | if [ "$WARPDRIVE_WSGI_STATIC_URL" != "" ]; then 304 | if [ "$WARPDRIVE_WSGI_STATIC_ROOT" != "" ]; then 305 | server_args --static-map $WARPDRIVE_WSGI_STATIC_URL=$WARPDRIVE_WSGI_STATIC_ROOT 306 | fi 307 | fi 308 | fi 309 | 310 | if [ "$WARPDRIVE_SERVER_TYPE" = "waitress" ]; then 311 | WARPDRIVE_WSGI_APPLICATION="$WARPDRIVE_WSGI_MODULE_NAME:$WARPDRIVE_WSGI_CALLABLE_OBJECT" 312 | 313 | if [ "$WARPDRIVE_WSGI_STATIC_URL" != "" ]; then 314 | if [ "$WARPDRIVE_WSGI_STATIC_ROOT" != "" ]; then 315 | create_whitenoise_wrapper 316 | WARPDRIVE_WSGI_APPLICATION="whitenoise_wrapper:application" 317 | PYTHONPATH="$PYTHONPATH:$WARPDRIVE_APP_ROOT/tmp/modules" 318 | export PYTHONPATH 319 | fi 320 | fi 321 | 322 | server_args $WARPDRIVE_WSGI_APPLICATION 323 | fi 324 | 325 | WARPDRIVE_DEPLOY_MODE="$WARPDRIVE_SERVER_TYPE" 326 | fi 327 | 328 | # If was doing auto detection and couldn't match any possible way to 329 | # host the Python web application, switch to 'mod_wsgi' server type and 330 | # show the default splash page. 331 | 332 | if [ "$WARPDRIVE_DEPLOY_MODE" = "auto" ]; then 333 | WARPDRIVE_DEPLOY_MODE="mod_wsgi" 334 | WARPDRIVE_SERVER_ARGS="" 335 | fi 336 | 337 | # Finally, execute the startup script for the final server type. 338 | 339 | WARPDRIVE_START=`which $0` 340 | WARPDRIVE_SCRIPTS=`dirname $WARPDRIVE_START` 341 | 342 | echo " -----> Running server script start-$WARPDRIVE_DEPLOY_MODE" 343 | 344 | exec $WARPDRIVE_SCRIPTS/start-$WARPDRIVE_DEPLOY_MODE $WARPDRIVE_SERVER_ARGS 345 | -------------------------------------------------------------------------------- /warpdrive/etc/warpdrive-verify: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ x"$WARPDRIVE_DEBUG" != x"" ]; then 4 | set -x 5 | fi 6 | 7 | # Ensure that any failure within this script or a user provided script 8 | # causes this script to fail immediately. This eliminates the need to 9 | # check individual statuses for anything which is run and prematurely 10 | # exit. Note that the feature of bash to exit in this way isn't 11 | # foolproof. Ensure that you heed any advice in: 12 | # 13 | # http://mywiki.wooledge.org/BashFAQ/105 14 | # http://fvue.nl/wiki/Bash:_Error_handling 15 | # 16 | # and use best practices to ensure that failures are always detected. 17 | # Any user supplied scripts should also use this failure mode. 18 | 19 | set -eo pipefail 20 | 21 | # Setup the environment if not already done. 22 | 23 | if [ x"$WARPDRIVE_ACTION" = x"" ]; then 24 | eval "$(warpdrive env)" 25 | fi 26 | 27 | WARPDRIVE_ACTION=verify 28 | export WARPDRIVE_ACTION 29 | 30 | # Make sure we are in the correct working directory for the application. 31 | 32 | cd $WARPDRIVE_SRC_ROOT 33 | 34 | # Run any user supplied script which verifies application by running 35 | # tests. The script must be executable in order to be run. It is not 36 | # possible for this script to change the permissions so it is executable 37 | # and then run it, due to some docker bug which results in the text file 38 | # being busy. For more details see: 39 | # 40 | # https://github.com/docker/docker/issues/9547 41 | 42 | if [ -f .warpdrive/action_hooks/verify ]; then 43 | if [ ! -x .warpdrive/action_hooks/verify ]; then 44 | echo "WARNING: Script .warpdrive/action_hooks/verify not executable." 45 | fi 46 | fi 47 | 48 | if [ -x .warpdrive/action_hooks/verify ]; then 49 | echo " -----> Running .warpdrive/action_hooks/verify" 50 | exec .warpdrive/action_hooks/verify 51 | fi 52 | -------------------------------------------------------------------------------- /warpdrive/etc/warpdrive-wheels: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ x"$WARPDRIVE_DEBUG" != x"" ]; then 4 | set -x 5 | fi 6 | 7 | # This is the script used to generate a set of Python wheels for any 8 | # nominated packages. 9 | 10 | # Ensure that any failure within this script or a user provided script 11 | # causes this script to fail immediately. This eliminates the need to 12 | # check individual statuses for anything which is run and prematurely 13 | # exit. Note that the feature of bash to exit in this way isn't 14 | # foolproof. Ensure that you heed any advice in: 15 | # 16 | # http://mywiki.wooledge.org/BashFAQ/105 17 | # http://fvue.nl/wiki/Bash:_Error_handling 18 | # 19 | # and use best practices to ensure that failures are always detected. 20 | # Any user supplied scripts should also use this failure mode. 21 | 22 | set -eo pipefail 23 | 24 | # Root directory for the runtime environment of the application. 25 | 26 | WARPDRIVE_APP_ROOT=${WARPDRIVE_APP_ROOT:-/opt/app-root} 27 | 28 | # Root directory for the source code of the application. 29 | 30 | WARPDRIVE_SRC_ROOT=${WARPDRIVE_SRC_ROOT:-$WARPDRIVE_APP_ROOT/src} 31 | 32 | # Make sure we are in the correct working directory for the application. 33 | 34 | cd $WARPDRIVE_SRC_ROOT 35 | 36 | # Activate the Python virtual environment if one has been created. 37 | 38 | if [ -f $WARPDRIVE_APP_ROOT/bin/activate ]; then 39 | . $WARPDRIVE_APP_ROOT/bin/activate 40 | fi 41 | 42 | # Run any user supplied script to be run prior to installing application 43 | # dependencies. This is to allow additional system packages to be 44 | # installed that may be required by any Python modules which are being 45 | # installed. The script must be executable in order to be run. It is not 46 | # possible for this script to change the permissions so it is executable 47 | # and then run it, due to some docker bug which results in the text file 48 | # being busy. For more details see: 49 | # 50 | # https://github.com/docker/docker/issues/9547 51 | 52 | if [ -f ${WARPDRIVE_SRC_ROOT}/.warpdrive/action_hooks/install ]; then 53 | if [ ! -x ${WARPDRIVE_SRC_ROOT}/.warpdrive/action_hooks/install ]; then 54 | echo "WARNING: Script ${WARPDRIVE_SRC_ROOT}/.warpdrive/action_hooks/install not executable." 55 | fi 56 | fi 57 | 58 | if [ -x ${WARPDRIVE_SRC_ROOT}/.warpdrive/action_hooks/install ]; then 59 | echo " -----> Running ${WARPDRIVE_SRC_ROOT}/.warpdrive/action_hooks/install" 60 | ${WARPDRIVE_SRC_ROOT}/.warpdrive/action_hooks/install 61 | fi 62 | 63 | # Create and populate the wheelhouse directory. 64 | 65 | WARPDRIVE_PIP_PACKAGES=${WARPDRIVE_PIP_PACKAGES:-requirements.txt} 66 | WARPDRIVE_PIP_WHEELS=${WARPDRIVE_PIP_WHEELS:-$WARPDRIVE_PIP_PACKAGES} 67 | 68 | WARPDRIVE_WHEEL_DIR=${WARPDRIVE_WHEEL_DIR:-$WARPDRIVE_SRC_ROOT/.warpdrive/wheelhouse} 69 | 70 | WARPDRIVE_BLD_ROOT=${WARPDRIVE_BLD_ROOT:-/tmp/warpdrive-wheelhouse.$$} 71 | 72 | mkdir -p $WARPDRIVE_WHEEL_DIR 73 | 74 | WARPDRIVE_PIP_OPTIONS= 75 | 76 | if [ "$WARPDRIVE_BUILD_TARGET" = "wheelhouse" ]; then 77 | WARPDRIVE_PIP_OPTIONS="--cache-dir $WARPDRIVE_SRC_ROOT/.warpdrive/packages" 78 | fi 79 | 80 | for filename in $WARPDRIVE_PIP_WHEELS; do 81 | if [ -f $filename ]; then 82 | echo " -----> Installing dependencies as wheels with pip ($filename)" 83 | pip wheel $WARPDRIVE_PIP_OPTIONS --wheel-dir $WARPDRIVE_WHEEL_DIR \ 84 | --exists-action=w --src=$WARPDRIVE_BLD_ROOT -r $filename 85 | fi 86 | done 87 | 88 | # Clean up any temporary files, including the results of checking out 89 | # any source code repositories when doing a 'pip install' from a VCS. 90 | 91 | rm -rf $WARPDRIVE_BLD_ROOT 92 | --------------------------------------------------------------------------------