diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 000000000..e0d5f5a7c --- /dev/null +++ b/.dockerignore @@ -0,0 +1,3 @@ +.git +docs/ +tests/ diff --git a/cookiecutter.json b/cookiecutter.json index 5c931f2df..cf3394635 100644 --- a/cookiecutter.json +++ b/cookiecutter.json @@ -8,6 +8,7 @@ "version": "0.1.0", "open_source_license": ["Apache Software License 2.0", "MIT license", "BSD license", "ISC license", "GNU General Public License v3"], "http_port": "5000", - "https_port": "2{{ cookiecutter.http_port }}", - "output_port": "8090" + "_copy_without_render": [ + "{{cookiecutter.project_slug}}/templates/*.cfg" + ] } diff --git a/{{cookiecutter.project_slug}}/Dockerfile b/{{cookiecutter.project_slug}}/Dockerfile index 5e98604e6..2a5800c22 100644 --- a/{{cookiecutter.project_slug}}/Dockerfile +++ b/{{cookiecutter.project_slug}}/Dockerfile @@ -1,47 +1,33 @@ # vim:set ft=dockerfile: -FROM birdhouse/bird-base:latest +FROM continuumio/miniconda3 MAINTAINER https://github.com/{{ cookiecutter.github_username }}/{{ cookiecutter.project_slug }} +LABEL Description="{{ cookiecutter.project_slug }} WPS" Vendor="Birdhouse" Version="{{ cookiecutter.version }}" -LABEL Description="{{ cookiecutter.project_slug }} WPS" Vendor="Birdhouse" +# Update Debian system +RUN apt-get update && apt-get install -y \ + build-essential \ +&& rm -rf /var/lib/apt/lists/* -# Configure hostname and ports for services -ENV HTTP_PORT 5000 -ENV OUTPUT_PORT 8000 -ENV HOSTNAME localhost +# Update conda +RUN conda update -n base conda -# Set current home -ENV HOME /root +# Copy WPS project +COPY . /opt/wps -# Copy application sources -COPY . /opt/birdhouse/src/{{ cookiecutter.project_slug }} +WORKDIR /opt/wps -# cd into application -WORKDIR /opt/birdhouse/src/{{ cookiecutter.project_slug }} +# Create conda environment +RUN conda env create -n wps -f environment.yml -# Provide custom.cfg with settings for docker image -COPY .docker.cfg custom.cfg +# Install WPS +RUN ["/bin/bash", "-c", "source activate wps && python setup.py develop"] -# Install system dependencies -RUN bash bootstrap.sh -i +# Start WPS service on port {{ cookiecutter.http_port }} on 0.0.0.0 +EXPOSE {{ cookiecutter.http_port }} +ENTRYPOINT ["/bin/bash", "-c"] +CMD ["source activate wps && exec emu --config /opt/wps/etc/demo.cfg"] -# Set conda enviroment -ENV ANACONDA_HOME /opt/conda -ENV CONDA_ENVS_DIR /opt/conda/envs - -# Run install and fix permissions -RUN make clean install && chmod 755 /opt/birdhouse/etc && chmod 755 /opt/birdhouse/var/run - -# Volume for data, cache, logfiles, ... -VOLUME /opt/birdhouse/var/lib -VOLUME /opt/birdhouse/var/log -# Volume for configs -VOLUME /opt/birdhouse/etc - -# Ports used in birdhouse -EXPOSE $HTTP_PORT $OUTPUT_PORT - -# Start supervisor in foreground -ENV DAEMON_OPTS --nodaemon - -# Start service ... -CMD ["make", "update-config", "start"] +# docker build -t {{ cookiecutter.github_username }}/{{ cookiecutter.project_slug }} . +# docker run -p {{ cookiecutter.http_port }}:{{ cookiecutter.http_port }} {{ cookiecutter.github_username }}/{{ cookiecutter.project_slug }} +# http://localhost:{{ cookiecutter.http_port }}/wps?request=GetCapabilities&service=WPS +# http://localhost:{{ cookiecutter.http_port }}/wps?request=DescribeProcess&service=WPS&identifier=all&version=1.0.0 diff --git a/{{cookiecutter.project_slug}}/Makefile b/{{cookiecutter.project_slug}}/Makefile index 9eafd10ee..00cb63e45 100644 --- a/{{cookiecutter.project_slug}}/Makefile +++ b/{{cookiecutter.project_slug}}/Makefile @@ -1,51 +1,26 @@ -VERSION := 0.3.16 -RELEASE := master - -# Include custom config if it is available --include Makefile.config - # Application APP_ROOT := $(CURDIR) APP_NAME := $(shell basename $(APP_ROOT)) -# guess OS (Linux, Darwin, ...) -OS_NAME := $(shell uname -s 2>/dev/null || echo "unknown") -CPU_ARCH := $(shell uname -m 2>/dev/null || uname -p 2>/dev/null || echo "unknown") - -# Python -SETUPTOOLS_VERSION := 36.5.0 -CONDA_VERSION := 4.5 -BUILDOUT_VERSION := 2.11 - # Anaconda ANACONDA_HOME ?= $(HOME)/anaconda CONDA_ENV ?= $(APP_NAME) -CONDA_ENVS_DIR ?= $(HOME)/.conda/envs -CONDA_ENV_PATH := $(CONDA_ENVS_DIR)/$(CONDA_ENV) -CONDA_PINNED := $(APP_ROOT)/requirements/conda_pinned -# Configuration used by update-config -HOSTNAME ?= localhost -HTTP_PORT ?= 8094 -OUTPUT_PORT ?= 8090 - -# choose anaconda installer depending on your OS +# Choose Anaconda installer depending on your OS ANACONDA_URL = https://repo.continuum.io/miniconda ifeq "$(OS_NAME)" "Linux" -FN := Miniconda2-latest-Linux-x86_64.sh +FN := Miniconda3-latest-Linux-x86_64.sh else ifeq "$(OS_NAME)" "Darwin" -FN := Miniconda2-latest-MacOSX-x86_64.sh +FN := Miniconda3-latest-MacOSX-x86_64.sh else FN := unknown endif -# Buildout files and folders -DOWNLOAD_CACHE := $(APP_ROOT)/downloads -BUILDOUT_FILES := parts eggs develop-eggs bin .installed.cfg .mr.developer.cfg *.egg-info bootstrap-buildout.py *.bak.* $(DOWNLOAD_CACHE) +TEMP_FILES := *.egg-info *.log *.sqlite # end of configuration -.DEFAULT_GOAL := help +.DEFAULT_GOAL := all .PHONY: all all: help @@ -54,82 +29,15 @@ all: help help: @echo "Please use \`make ' where is one of" @echo " help to print this help message. (Default)" - @echo " version to print version number of this Makefile." - @echo " info to print information about $(APP_NAME)." - @echo " install to install $(APP_NAME) by running 'bin/buildout -c custom.cfg'." - @echo " sysinstall to install system packages from requirements.sh. You can also call 'bash requirements.sh' directly." - @echo " update to update your application by running 'bin/buildout -o -c custom.cfg' (buildout offline mode)." - @echo " clean to delete all files that are created by running buildout." + @echo " install to install $(APP_NAME) by running 'python setup.py develop'." + @echo " start to start $(APP_NAME) as daemon service." + @echo " clean to remove *all* files that are not controlled by 'git'. WARNING: use it *only* if you know what you do!" @echo "\nTesting targets:" @echo " test to run tests (but skip long running tests)." @echo " testall to run all tests (including long running tests)." @echo " pep8 to run pep8 code style checks." @echo "\nSphinx targets:" @echo " docs to generate HTML documentation with Sphinx." - @echo " linkcheck to check all external links in documentation for integrity." - @echo " doc8 to run doc8 documentation style checks." - @echo "\nSupporting targets:" - @echo " envclean to remove the conda enviroment $(CONDA_ENV)." - @echo " srcclean to remove all *.pyc files." - @echo " distclean to remove *all* files that are not controlled by 'git'. WARNING: use it *only* if you know what you do!" - @echo " passwd to generate password for 'phoenix-password' in custom.cfg." - @echo " export to export the conda environment. Caution! You always need to check it the enviroment.yml is working." - @echo " selfupdate to update this Makefile." - @echo "\nSupervisor targets:" - @echo " start to start supervisor service." - @echo " stop to stop supervisor service." - @echo " restart to restart supervisor service." - @echo " status to show supervisor status" - -.PHONY: version -version: - @echo "Version: $(VERSION)" - -.PHONY: info -info: - @echo "Informations about your Bird:" - @echo " OS_NAME $(OS_NAME)" - @echo " CPU_ARCH $(CPU_ARCH)" - @echo " Anaconda Home $(ANACONDA_HOME)" - @echo " Conda Environment $(CONDA_ENV). Use \`source activate $(CONDA_ENV)' to activate it." - @echo " Conda Prefix $(CONDA_ENV_PATH)" - @echo " APP_NAME $(APP_NAME)" - @echo " APP_ROOT $(APP_ROOT)" - @echo " DOWNLOAD_CACHE $(DOWNLOAD_CACHE)" - -## Helper targets ... ensure that Makefile etc are in place - -.PHONY: backup -backup: - @echo "Backup custom config ..." - @-test -f custom.cfg && cp -v --update --backup=numbered --suffix=.bak custom.cfg custom.cfg.bak - -.PHONY: .gitignore -.gitignore: - @echo "Setup default .gitignore ..." - @curl "https://raw.githubusercontent.com/bird-house/birdhousebuilder.bootstrap/$(RELEASE)/dot_gitignore" --silent --insecure --output .gitignore - -.PHONY: bootstrap.sh -bootstrap.sh: - @echo "Update bootstrap.sh ..." - @curl "https://raw.githubusercontent.com/bird-house/birdhousebuilder.bootstrap/$(RELEASE)/bootstrap.sh" --silent --insecure --output bootstrap.sh "https://raw.githubusercontent.com/bird-house/birdhousebuilder.bootstrap/$(RELEASE)/bootstrap.sh" - @chmod 755 bootstrap.sh - -custom.cfg: - @echo "Using custom.cfg for buildout ..." - @test -f custom.cfg || cp -v custom.cfg.example custom.cfg - -.PHONY: downloads -downloads: - @echo "Using DOWNLOAD_CACHE $(DOWNLOAD_CACHE)" - @test -d $(DOWNLOAD_CACHE) || mkdir -v -p $(DOWNLOAD_CACHE) - -.PHONY: init -init: custom.cfg downloads - -bootstrap-buildout.py: - @echo "Update buildout bootstrap-buildout.py ..." - @test -f boostrap-buildout.py || curl https://bootstrap.pypa.io/bootstrap-buildout.py --insecure --silent --output bootstrap-buildout.py ## Anaconda targets @@ -138,78 +46,47 @@ anaconda: @echo "Installing Anaconda ..." @test -d $(ANACONDA_HOME) || curl $(ANACONDA_URL)/$(FN) --silent --insecure --output "$(DOWNLOAD_CACHE)/$(FN)" @test -d $(ANACONDA_HOME) || bash "$(DOWNLOAD_CACHE)/$(FN)" -b -p $(ANACONDA_HOME) - @echo "Add '$(ANACONDA_HOME)/bin' to your PATH variable in '.bashrc'." - -.PHONY: conda_config -conda_config: anaconda - @echo "Update ~/.condarc" - @-"$(ANACONDA_HOME)/bin/conda" install -y conda=$(CONDA_VERSION) requests - @"$(ANACONDA_HOME)/bin/conda" config --add envs_dirs $(CONDA_ENVS_DIR) - @"$(ANACONDA_HOME)/bin/conda" config --set ssl_verify true - @"$(ANACONDA_HOME)/bin/conda" config --set use_pip true - @"$(ANACONDA_HOME)/bin/conda" config --set channel_priority true - @"$(ANACONDA_HOME)/bin/conda" config --set auto_update_conda false - @"$(ANACONDA_HOME)/bin/conda" config --add channels defaults - @"$(ANACONDA_HOME)/bin/conda" config --append channels birdhouse - @"$(ANACONDA_HOME)/bin/conda" config --append channels conda-forge + @echo "Please add '$(ANACONDA_HOME)/bin' to your PATH variable in '.bashrc'." .PHONY: conda_env -conda_env: anaconda conda_config - @echo "Update conda environment $(CONDA_ENV) ..." - @test -d $(CONDA_ENV_PATH) || "$(ANACONDA_HOME)/bin/conda" env create -n $(CONDA_ENV) -f environment.yml - "$(ANACONDA_HOME)/bin/conda" install -y -n $(CONDA_ENV) setuptools=$(SETUPTOOLS_VERSION) - -.PHONY: conda_pinned -conda_pinned: conda_env - @echo "Update pinned conda packages ..." - @-test -d $(CONDA_ENV_PATH) && test -f $(CONDA_PINNED) && cp -f "$(CONDA_PINNED)" "$(CONDA_ENV_PATH)/conda-meta/pinned" - -.PHONY: export -export: - @echo "Exporting conda enviroment ..." - @test -d $(CONDA_ENV_PATH) && "$(ANACONDA_HOME)/bin/conda" env export -n $(CONDA_ENV) -f environment.yml +conda_env: anaconda + @echo "Updating conda environment $(CONDA_ENV) ..." + "$(ANACONDA_HOME)/bin/conda" env update -n $(CONDA_ENV) -f environment.yml ## Build targets .PHONY: bootstrap -bootstrap: init conda_env conda_pinned bootstrap-buildout.py - @echo "Bootstrap buildout ..." - @test -f bin/buildout || bash -c "source $(ANACONDA_HOME)/bin/activate $(CONDA_ENV);python bootstrap-buildout.py -c custom.cfg --allow-site-packages --setuptools-version=$(SETUPTOOLS_VERSION) --buildout-version=$(BUILDOUT_VERSION)" +bootstrap: conda_env bootstrap_dev + @echo "Bootstrap ..." -.PHONY: sysinstall -sysinstall: - @echo "\nInstalling system packages for bootstrap ..." - @bash bootstrap.sh -i - @echo "\nInstalling system packages for your application ..." - @-test -f requirements.sh && bash requirements.sh +.PHONY: bootstrap_dev +bootstrap_dev: + @echo "Installing development requirements for tests and docs ..." + @-bash -c "$(ANACONDA_HOME)/bin/conda install -y -n $(CONDA_ENV) pytest flake8 sphinx" + @-bash -c "source $(ANACONDA_HOME)/bin/activate $(CONDA_ENV) && pip install -r requirements_dev.txt" .PHONY: install install: bootstrap - @echo "Installing application with buildout ..." - @-bash -c "source $(ANACONDA_HOME)/bin/activate $(CONDA_ENV);bin/buildout buildout:anaconda-home=$(ANACONDA_HOME) -c custom.cfg" + @echo "Installing application ..." + @-bash -c "source $(ANACONDA_HOME)/bin/activate $(CONDA_ENV) && python setup.py develop" @echo "\nStart service with \`make start'" -.PHONY: update -update: - @echo "Update application config with buildout (offline mode) ..." - @-bash -c "source $(ANACONDA_HOME)/bin/activate $(CONDA_ENV);bin/buildout buildout:anaconda-home=$(ANACONDA_HOME) -o -c custom.cfg" - -.PHONY: update-config -update-config: - @echo "Update application config with buildout (offline mode) and environment variables..." - @-bash -c "source $(ANACONDA_HOME)/bin/activate $(CONDA_ENV);bin/buildout buildout:anaconda-home=$(ANACONDA_HOME) settings:hostname=$(HOSTNAME) settings:output-port=$(OUTPUT_PORT) settings:http-port=$(HTTP_PORT) -o -c custom.cfg" +.PHONY: start +start: + @echo "Starting application ..." + @-bash -c "source $(ANACONDA_HOME)/bin/activate $(CONDA_ENV) && $(APP_NAME) -a -d" .PHONY: clean clean: srcclean envclean - @echo "Cleaning buildout files ..." - @-for i in $(BUILDOUT_FILES); do \ - test -e $$i && rm -v -rf $$i; \ - done + @echo "Cleaning generated files ..." + @-for i in $(TEMP_FILES); do \ + test -e $$i && rm -v -rf $$i; \ + done .PHONY: envclean -envclean: stop +envclean: @echo "Removing conda env $(CONDA_ENV)" - @-test -d $(CONDA_ENV_PATH) && "$(ANACONDA_HOME)/bin/conda" remove -n $(CONDA_ENV) --yes --all + @-"$(ANACONDA_HOME)/bin/conda" remove -n $(CONDA_ENV) --yes --all .PHONY: srcclean srcclean: @@ -217,72 +94,32 @@ srcclean: @-find $(APP_ROOT) -type f -name "*.pyc" -print | xargs rm .PHONY: distclean -distclean: backup clean - @echo "Cleaning distribution ..." +distclean: clean + @echo "Cleaning ..." @git diff --quiet HEAD || echo "There are uncommited changes! Not doing 'git clean' ..." - @-git clean -dfx -e *.bak -e custom.cfg -e Makefile.config + @-git clean -dfx -e *.bak -e custom.cfg -.PHONY: passwd -passwd: custom.cfg - @echo "Generate Phoenix password ..." - @echo "Enter a password with at least 8 characters." - @bash -c "source $(ANACONDA_HOME)/bin/activate $(CONDA_ENV); python -c 'from IPython.lib import passwd; pw = passwd(algorithm=\"sha256\"); lines = [\"phoenix-password = \" + pw + \"\\n\" if line.startswith(\"phoenix-password\") else line for line in open(\"custom.cfg\", \"r\")]; file = open(\"custom.cfg\", \"w\"); file.writelines(lines); file.close()'" - @echo "" - @echo "Run \`make install restart' to activate this password." +## Test targets .PHONY: test test: @echo "Running tests (skip slow and online tests) ..." - bash -c "source $(ANACONDA_HOME)/bin/activate $(CONDA_ENV); bin/py.test -v -m 'not slow and not online'" + @-bash -c "source $(ANACONDA_HOME)/bin/activate $(CONDA_ENV);pytest -v -m 'not slow and not online'" .PHONY: testall testall: @echo "Running all tests (including slow and online tests) ..." - bash -c "source $(ANACONDA_HOME)/bin/activate $(CONDA_ENV); bin/py.test -v" + @-bash -c "source $(ANACONDA_HOME)/bin/activate $(CONDA_ENV) && pytest -v" .PHONY: pep8 pep8: @echo "Running pep8 code style checks ..." - $(CONDA_ENV_PATH)/bin/flake8 + @-bash -c "source $(ANACONDA_HOME)/bin/activate $(CONDA_ENV) && flake8" + +## Sphinx targets .PHONY: docs docs: @echo "Generating docs with Sphinx ..." - $(MAKE) -C $@ clean html + @-bash -c "source $(ANACONDA_HOME)/bin/activate $(CONDA_ENV);$(MAKE) -C $@ clean html" @echo "open your browser: firefox docs/build/html/index.html" - -.PHONY: linkcheck -linkcheck: - @echo "Run link checker on docs..." - $(MAKE) -C docs linkcheck - -.PHONY: doc8 -doc8: - @echo "Running doc8 doc style checks ..." - $(CONDA_ENV_PATH)/bin/doc8 docs/ - -.PHONY: selfupdate -selfupdate: bootstrap.sh .gitignore - @curl "https://raw.githubusercontent.com/bird-house/birdhousebuilder.bootstrap/$(RELEASE)/Makefile" --silent --insecure --output Makefile - -## Supervisor targets - -.PHONY: start -start: - @echo "Starting supervisor service ..." - bin/supervisord start - -.PHONY: stop -stop: - @echo "Stopping supervisor service ..." - -bin/supervisord stop - -.PHONY: restart -restart: - @echo "Restarting supervisor service ..." - bin/supervisord restart - -.PHONY: status -status: - @echo "Supervisor status ..." - bin/supervisorctl status diff --git a/{{cookiecutter.project_slug}}/Makefile.config.example b/{{cookiecutter.project_slug}}/Makefile.config.example deleted file mode 100644 index fccdcaa54..000000000 --- a/{{cookiecutter.project_slug}}/Makefile.config.example +++ /dev/null @@ -1,3 +0,0 @@ -# Anaconda -ANACONDA_HOME ?= /opt/anaconda -CONDA_ENVS_DIR ?= /opt/anaconda/envs diff --git a/{{cookiecutter.project_slug}}/bootstrap.sh b/{{cookiecutter.project_slug}}/bootstrap.sh deleted file mode 100755 index 8f4dc1c0e..000000000 --- a/{{cookiecutter.project_slug}}/bootstrap.sh +++ /dev/null @@ -1,67 +0,0 @@ -#!/bin/bash - -usage() { - cat <`_ and run as the installation user. +Install from GitHub +------------------- -Now, check out the {{ cookiecutter.project_slug }} code from GitHub and start the installation: +Check out code from the {{ cookiecutter.project_name }} GitHub repo and start the installation: .. code-block:: sh $ git clone https://github.com/{{ cookiecutter.github_username }}/{{ cookiecutter.project_slug }}.git $ cd {{ cookiecutter.project_slug }} - $ make clean install + $ conda env create -f environment.yml + $ source activate {{ cookiecutter.project_slug }} + $ python setup.py develop -After successful installation you need to start the services: +Install the lazy way +-------------------- + +The previous installation instructions assume you have Anaconda installed. +We provide also a ``Makefile`` to run this installation without additional steps: .. code-block:: sh - $ make start # starts supervisor services - $ make status # shows supervisor status + $ git clone https://github.com/{{ cookiecutter.github_username }}/{{ cookiecutter.project_slug }}.git + $ cd {{ cookiecutter.project_slug }} + $ make clean # cleans up a previous Conda environment + $ make install # installs Conda if necessary and runs the above installation steps -The depolyed WPS service is by default available on http://localhost:{{ cookiecutter.http_port }}/wps?service=WPS&version=1.0.0&request=GetCapabilities. +Start {{ cookiecutter.project_name }} PyWPS service +----------------------- -Check the log files for errors: +After successful installation you can start the service using the ``{{ cookiecutter.project_slug }}`` command-line. .. code-block:: sh - $ tail -f ~/birdhouse/var/log/pywps/{{ cookiecutter.project_slug }}.log - $ tail -f ~/birdhouse/var/log/supervisor/{{ cookiecutter.project_slug }}.log + $ {{ cookiecutter.project_slug }} --help # show help + $ {{ cookiecutter.project_slug }} # start service with default configuration -You will find more information about the installation in the `Makefile documentation `_. + OR -Non-default installation ------------------------- + $ {{ cookiecutter.project_slug }} --daemon # start service as daemon + loading configuration + forked process id: 42 -You can customize the installation to use different ports, locations and run user. +The deployed WPS service is by default available on: -To change the anaconda location edit the ``Makefile.config``, for example:: +http://localhost:{{ cookiecutter.http_port }}/wps?service=WPS&version=1.0.0&request=GetCapabilities. - ANACONDA_HOME ?= /opt/anaconda - CONDA_ENVS_DIR ?= /opt/anaconda/envs +.. NOTE:: Remember the process ID (PID) so you can stop the service with ``kill PID``. -You can install {{ cookiecutter.project_slug }} as ``root`` and run it as unprivileged user like ``www-data``: +Check the log files for errors: .. code-block:: sh - root$ mkdir -p /opt/birdhouse/src - root$ cd /opt/birdhouse/src - root$ git clone https://github.com/bird-house/{{ cookiecutter.project_slug }}.git - root$ cd {{ cookiecutter.project_slug }} + $ tail -f pywps.log -Edit ``custom.cfg``: +Run {{ cookiecutter.project_name }} as Docker container +--------------------------- -.. code-block:: ini +You can also run {{ cookiecutter.project_name }} as a Docker container, see the :ref:`Tutorial `. - [buildout] - extends = buildout.cfg - - [settings] - hostname = {{ cookiecutter.project_slug }} - http-port = 80 - output-port = 8000 - log-level = WARN - - # deployment options - prefix = /opt/birdhouse - user = www-data - etc-user = root - -Run the installtion and start the services: - -.. code-block:: sh +Use Ansible to deploy {{ cookiecutter.project_name }} on your System +---------------------------------------- - root$ make clean install - root$ make start # stop or restart - root$ make status +Use the `Ansible playbook`_ for PyWPS to deploy {{ cookiecutter.project_name }} on your system. +Follow the `example`_ for {{ cookiecutter.project_name }} given in the playbook. -.. _Anaconda: https://www.continuum.io/ +.. _Ansible playbook: http://ansible-wps-playbook.readthedocs.io/en/latest/index.html +.. _example: http://ansible-wps-playbook.readthedocs.io/en/latest/tutorial.html diff --git a/{{cookiecutter.project_slug}}/environment.yml b/{{cookiecutter.project_slug}}/environment.yml index 7882d0566..41f2d88b7 100644 --- a/{{cookiecutter.project_slug}}/environment.yml +++ b/{{cookiecutter.project_slug}}/environment.yml @@ -4,32 +4,6 @@ channels: - conda-forge - defaults dependencies: -- python=2.7 -- pyopenssl=0.16 -- cryptography=1.4 -- pyyaml=3.11 -- curl=7.45.0 -- icu=58 -# buildout -- mako -# tests -- pytest -# pep8 -- flake8 -# py2/3 compat -- six -# docs -- sphinx -- doc8 -# wps -- pywps=4.0.0 -- click=6 -- nginx=1.10.3 -- supervisor=3.3 -- gunicorn=19 -- lxml -- libxslt -- genshi=0.7 -- pip: - - sphinx-autoapi==0.5.0 - - -e git+https://github.com/huard/sphinx-autodoc-pywps.git#egg=sphinx_autodoc_pywps +- pywps=4.1.0 +- jinja2 +- click diff --git a/{{cookiecutter.project_slug}}/etc/debug.cfg b/{{cookiecutter.project_slug}}/etc/debug.cfg new file mode 100644 index 000000000..c00a2b7ff --- /dev/null +++ b/{{cookiecutter.project_slug}}/etc/debug.cfg @@ -0,0 +1,2 @@ +[logging] +level = DEBUG diff --git a/{{cookiecutter.project_slug}}/etc/demo.cfg b/{{cookiecutter.project_slug}}/etc/demo.cfg new file mode 100644 index 000000000..52145a38e --- /dev/null +++ b/{{cookiecutter.project_slug}}/etc/demo.cfg @@ -0,0 +1,2 @@ +[logging] +level = WARN diff --git a/{{cookiecutter.project_slug}}/etc/sample-custom.cfg b/{{cookiecutter.project_slug}}/etc/sample-custom.cfg new file mode 100644 index 000000000..e708743cd --- /dev/null +++ b/{{cookiecutter.project_slug}}/etc/sample-custom.cfg @@ -0,0 +1,6 @@ +[server] +url = http://demo.org:{{ cookiecutter.http_port }}/wps +outputurl = http://demo.org:{{ cookiecutter.http_port }}/outputs + +[logging] +level = DEBUG diff --git a/{{cookiecutter.project_slug}}/etc/sample-postgres.cfg b/{{cookiecutter.project_slug}}/etc/sample-postgres.cfg new file mode 100644 index 000000000..a16b9049f --- /dev/null +++ b/{{cookiecutter.project_slug}}/etc/sample-postgres.cfg @@ -0,0 +1,3 @@ +[logging] +level = INFO +database = postgresql+psycopg2://postgres:postgres@localhost:5432/postgres diff --git a/{{cookiecutter.project_slug}}/requirements.txt b/{{cookiecutter.project_slug}}/requirements.txt index bc7979b5f..3a41542b5 100644 --- a/{{cookiecutter.project_slug}}/requirements.txt +++ b/{{cookiecutter.project_slug}}/requirements.txt @@ -1,3 +1,3 @@ pywps>=4.0.0 -werkzeug +jinja2 click diff --git a/{{cookiecutter.project_slug}}/requirements_dev.txt b/{{cookiecutter.project_slug}}/requirements_dev.txt index 1b8285739..bface23c4 100644 --- a/{{cookiecutter.project_slug}}/requirements_dev.txt +++ b/{{cookiecutter.project_slug}}/requirements_dev.txt @@ -1,5 +1,5 @@ pytest flake8 -sphinx +sphinx>=1.7 +sphinx-autoapi -e git+https://github.com/huard/sphinx-autodoc-pywps.git#egg=sphinx_autodoc_pywps -bumpversion diff --git a/{{cookiecutter.project_slug}}/setup.py b/{{cookiecutter.project_slug}}/setup.py index 3fa9dfb80..9a1af58f0 100644 --- a/{{cookiecutter.project_slug}}/setup.py +++ b/{{cookiecutter.project_slug}}/setup.py @@ -59,5 +59,5 @@ install_requires=reqs, entry_points={ 'console_scripts': [ - '{{ cookiecutter.project_slug }}={{ cookiecutter.project_slug }}:main', + '{{ cookiecutter.project_slug }}={{ cookiecutter.project_slug }}:cli', ]},) diff --git a/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/__init__.py b/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/__init__.py index 13ed049d3..ccf218463 100644 --- a/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/__init__.py +++ b/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/__init__.py @@ -3,7 +3,7 @@ """Top-level package for {{ cookiecutter.project_name }}.""" from .wsgi import application -from .cli import main +from .cli import cli __author__ = """{{ cookiecutter.full_name }}""" __email__ = '{{ cookiecutter.email }}' diff --git a/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/cli.py b/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/cli.py index 42d6b201a..512ee1f56 100644 --- a/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/cli.py +++ b/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/cli.py @@ -6,22 +6,33 @@ ########################################################### import os - +import click +from jinja2 import Environment, PackageLoader, select_autoescape from pywps import configuration from . import wsgi from six.moves.urllib.parse import urlparse -import logging -logging.basicConfig(format='%(message)s', level=logging.INFO) -LOGGER = logging.getLogger('DEMO') +template_env = Environment( + loader=PackageLoader('{{ cookiecutter.project_slug }}', 'templates'), + autoescape=select_autoescape(['yml', 'xml']) +) + + +def write_user_config(**kwargs): + config_templ = template_env.get_template('pywps.cfg') + rendered_config = config_templ.render(**kwargs) + config_file = os.path.abspath(os.path.join(os.path.curdir, "custom.cfg")) + with open(config_file, 'w') as fp: + fp.write(rendered_config) + return config_file def get_host(): url = configuration.get_config_value('server', 'url') - url = url or 'http://localhost:5000/wps' + url = url or 'http://localhost:{{ cookiecutter.http_port }}/wps' - LOGGER.warn("starting WPS service on %s", url) + click.echo("starting WPS service on {}".format(url)) parsed_url = urlparse(url) if ':' in parsed_url.netloc: @@ -46,56 +57,66 @@ def _run(application, bind_host=None, daemon=False): hostname=bind_host, port=port, application=application, - use_debugger=True, - use_reloader=True, + use_debugger=False, + use_reloader=False, + threaded=True, + # processes=2, use_evalex=not daemon, static_files=static_files) -def main(): - import argparse - - parser = argparse.ArgumentParser( - description="""Script for starting a demo WPS instance. - This service is by default available at http://localhost:5000/wps""", - epilog="""Do not use this service in a production environment. - It's intended to be running in a test environment only! - For more documentation, visit http://bird-house.github.io/ - """ - ) - parser.add_argument('--debug', - action="store_true", help="enable debug logging mode") - parser.add_argument('-c', '--config', - help="path to pywps configuration file") - parser.add_argument('-a', '--all-addresses', - action='store_true', help="run service using IPv4 0.0.0.0 (all network interfaces), " - "otherwise bind to 127.0.0.1 (localhost).") - parser.add_argument('-d', '--daemon', - action='store_true', help="run in daemon mode") - args = parser.parse_args() +@click.command() +@click.option('--config', metavar='PATH', help='path to pywps configuration file.') +@click.option('--bind-host', metavar='IP-ADDRESS', default='0.0.0.0', + help='IP address used to bind service.') +@click.option('--daemon/--no-daemon', default=False, help='run in daemon mode.') +@click.option('--hostname', metavar='HOSTNAME', default='localhost', help='hostname in PyWPS configuration.') +@click.option('--port', metavar='PORT', default='{{ cookiecutter.http_port }}', help='port in PyWPS configuration.') +@click.option('--maxsingleinputsize', default='200mb', help='maxsingleinputsize in PyWPS configuration.') +@click.option('--maxprocesses', metavar='INT', default='10', help='maxprocesses in PyWPS configuration.') +@click.option('--parallelprocesses', metavar='INT', default='2', help='parallelprocesses in PyWPS configuration.') +@click.option('--log-level', metavar='LEVEL', default='INFO', help='log level in PyWPS configuration.') +@click.option('--log-file', metavar='PATH', default='pywps.log', help='log file in PyWPS configuration.') +@click.option('--database', default='sqlite:///pywps-logs.sqlite', help='database in PyWPS configuration') +def cli(config, bind_host, daemon, hostname, port, + maxsingleinputsize, maxprocesses, parallelprocesses, + log_level, log_file, database): + """Command line for starting a PyWPS service. + This service is by default available at http://localhost:{{ cookiecutter.http_port }}/wps + + Do not use this service in a production environment. + It's intended to be running in a test environment only! + For more documentation, visit http://pywps.org/doc + """ cfgfiles = [] - if args.config: - cfgfiles.append(args.config) - LOGGER.warn('using pywps configuration: %s', args.config) - if args.debug: - cfgfiles.append(os.path.join(os.path.dirname(__file__), 'debug.cfg')) - if args.all_addresses: - bind_host = '0.0.0.0' - else: - bind_host = '127.0.0.1' + cfgfiles.append(write_user_config( + wps_hostname=hostname, + wps_port=port, + wps_maxsingleinputsize=maxsingleinputsize, + wps_maxprocesses=maxprocesses, + wps_parallelprocesses=parallelprocesses, + wps_log_level=log_level, + wps_log_file=log_file, + wps_database=database, + )) + if config: + cfgfiles.append(config) app = wsgi.create_app(cfgfiles) # let's start the service ... - if args.daemon: + # See: + # * https://github.com/geopython/pywps-flask/blob/master/demo.py + # * http://werkzeug.pocoo.org/docs/0.14/serving/ + if daemon: # daemon (fork) mode pid = None try: pid = os.fork() if pid: - LOGGER.warn('forked process id: %s', pid) + click.echo('forked process id: {}'.format(pid)) except OSError as e: raise Exception("%s [%d]" % (e.strerror, e.errno)) - if (pid == 0): + if pid == 0: os.setsid() _run(app, bind_host=bind_host, daemon=True) else: @@ -103,7 +124,3 @@ def main(): else: # no daemon _run(app, bind_host=bind_host) - - -if __name__ == '__main__': - main() diff --git a/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/default.cfg b/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/default.cfg index e94bdb8cd..1bc7f1bc4 100644 --- a/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/default.cfg +++ b/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/default.cfg @@ -7,12 +7,12 @@ provider_name = {{ cookiecutter.project_name }} provider_url=http://{{ cookiecutter.project_slug }}.readthedocs.org/en/latest/ [server] -url = http://localhost:5000/wps -outputurl = http://localhost:5000/outputs +url = http://localhost:{{ cookiecutter.http_port }}/wps +outputurl = http://localhost:{{ cookiecutter.http_port }}/outputs allowedinputpaths = / maxsingleinputsize = 200mb -sethomedir = true -setworkdir = true +maxprocesses = 10 +parallelprocesses = 2 [logging] level = INFO diff --git a/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/templates/pywps.cfg b/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/templates/pywps.cfg new file mode 100644 index 000000000..20951f8ae --- /dev/null +++ b/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/templates/pywps.cfg @@ -0,0 +1,27 @@ +[server] +{% if wps_url %} +url = {{ wps_url }} +{% else %} +url = http://{{ wps_hostname }}:{{ wps_port }}/wps +{% endif %} +{% if wps_outputurl %} +outputurl = {{ wps_outputurl }} +{% else %} +outputurl = http://{{ wps_hostname }}:{{ wps_port }}/outputs +{% endif %} +allowedinputpaths = / +maxsingleinputsize = {{ wps_maxsingleinputsize|default('200mb') }} +maxprocesses = {{ wps_maxprocesses|default('10') }} +parallelprocesses = {{ wps_parallelprocesses|default('2') }} +{% if wps_outputpath %} +outputpath= {{ wps_outputpath }} +{% endif %} +{% if wps_workdir %} +workdir={{ wps_workdir }} +{% endif %} + +[logging] +level = {{ wps_log_level|default('INFO') }} +file = {{ wps_log_file|default('pywps.log') }} +database = {{ wps_database|default('sqlite:///pywps-logs.sqlite') }} +format = %(asctime)s] [%(levelname)s] line=%(lineno)s module=%(module)s %(message)s