diff --git a/Dockerfile.autobuild b/Dockerfile.autobuild new file mode 100644 index 0000000..42dfba6 --- /dev/null +++ b/Dockerfile.autobuild @@ -0,0 +1,44 @@ +# ----------------------------------------------------------------------------- +# docker-pinry +# +# Builds a basic docker image that can run Pinry (http://getpinry.com) and serve +# all of it's assets, there are more optimal ways to do this but this is the +# most friendly and has everything contained in a single instance. +# +# Authors: Isaac Bythewood +# Updated: Mar 29th, 2016 +# Require: Docker (http://www.docker.io/) +# ----------------------------------------------------------------------------- + + +# Base system is the LTS version of Ubuntu. +FROM python:3.6-stretch + +RUN groupadd -g 2300 tmpgroup && usermod -g tmpgroup www-data && groupdel www-data && groupadd -g 1000 www-data && usermod -g www-data www-data && usermod -u 1000 www-data && groupdel tmpgroup + +RUN apt-get update +RUN apt-get -y install nginx nginx-extras pwgen + +RUN mkdir -p /srv/www/; cd /srv/www/; git clone https://github.com/pinry/pinry.git +RUN mkdir /srv/www/pinry/logs; mkdir /data +RUN cd /srv/www/pinry && pip install pipenv && pipenv install --three --system +RUN pip install gunicorn + +# Fix permissions +RUN chown -R www-data:www-data /srv/www + + +# Load in all of our config files. +ADD docker-contents/nginx/nginx.conf /etc/nginx/nginx.conf +ADD docker-contents/nginx/sites-enabled/default /etc/nginx/sites-enabled/default + +# Fix permissions +RUN mkdir /scripts/ +ADD docker-contents/scripts/* /scripts/ +RUN chown -R www-data:www-data /data +RUN mkdir /var/log/gunicorn + +# 80 is for nginx web, /data contains static files and database /start runs it. +expose 80 +volume ["/data"] +cmd ["/scripts/start.sh"] diff --git a/README.rst b/README.rst index e7ca02b..30276af 100644 --- a/README.rst +++ b/README.rst @@ -20,7 +20,7 @@ Feature Setup Guide for users -------------------------- -Please use docker to install `docker-pinry `_ +Please use docker to install `pinry `_ Developers or users who are familiar with python/nginx could setup Pinry with following guide : ) diff --git a/docker/CONTRIBUTORS.rst b/docker/CONTRIBUTORS.rst new file mode 100644 index 0000000..7a5a178 --- /dev/null +++ b/docker/CONTRIBUTORS.rst @@ -0,0 +1,14 @@ +Contributors +============ + +The core contributors for Pinry have been/currently are: + +* Isaac Bythewood +* Krzysztof Klimonda + +For a full list of contributors check out the `GitHub Contributors Graph`_. + +.. Links + +.. _GitHub Contributors Graph: https://github.com/pinry/pinry/graphs/contributors + diff --git a/docker/LICENSE.rst b/docker/LICENSE.rst new file mode 100644 index 0000000..f090ecc --- /dev/null +++ b/docker/LICENSE.rst @@ -0,0 +1,27 @@ +License (Simplified BSD) +======================== + +| Copyright (c) Pinry Contributors +| All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + diff --git a/docker/README.rst b/docker/README.rst new file mode 100644 index 0000000..c1e44ac --- /dev/null +++ b/docker/README.rst @@ -0,0 +1,135 @@ +Pinry Docker +============ + +.. image:: https://travis-ci.org/pinry/docker-pinry.svg?branch=master + :target: https://travis-ci.org/pinry/docker-pinry + +A nice and easy way to get a Pinry instance up and running using docker. For +help on getting started with docker see the `official getting started guide`_. +For more information on Pinry and a demo check out it's `website`_. + + +Getting Pinry Docker +--------------------- + +Running this will get the latest version of pinry itself:: + + git clone https://github.com/pinry/pinry + cd pinry + ./docker/bootstrap.sh + +Now you can start your container by command like this:: + + # this is where your database and pins localted + mkdir data + # use absolute path for docker to avoid using default data-volume (we use directory instead) + ./docker/start_docker.sh `readlink -f data` + +Please visit `http://your-ip` to visit your instance and register a new account, enjoy it. + + +Configuring docker-pinry +------------------------ +Enable signups for new users by editing ``pinry/local_settings.py``:: + + ALLOW_NEW_REGISTRATIONS = True + +`Additional pinry configuration settings`_ + +Building docker-pinry again +--------------------------- + +Running this will build you a docker image with the latest version of pinry:: + + ./docker/build_docker.sh + + +Running docker-pinry in manual way +---------------------------------- + +Running the start command for the first time will setup your production secret +key, database and static files. It is important that you decide what port you +want and what location on the host machine you wish to store your files. If this +is the only thing running on your system and you wish to make it public without +a proxy then you can set ``-p=80:80``. The setting ``-p=10000:80`` assumes you +are wanting to proxy to this instance using something like nginx. Also note that +you must have your host mount directory created first (``mkdir -p /mnt/pinry``) + +Then you have two choice to run docker-pinry + +Fist one, with automaticlly configured default arguments:: + + ./docker/start_docker.sh /mnt/pinry + + +Second one, start docker by hand with customized arguments:: + + SETTINGS_PATH=$(readlink -f docker/pinry/local_settings.py) \ + DATA_PATH=$(readlink -f /mnt/pinry) \ + sudo docker run -d=true -p=10000:80 \ + -v=${DATA_PATH}:/data \ + -v=${SETTINGS_PATH}:/srv/www/pinry/pinry/settings/local_settings.py \ + pinry/pinry /scripts/start.sh + +If it's the first run it'll take a few seconds but it will print out your +container ID which should be used to start and stop the container in the future +using the commands:: + + sudo docker start + sudo docker stop + + +Running docker-pinry with docker-compose +----------------------------------------- + + +Just config your ``docker-compose.yml`` and then run:: + + sudo pip install -U docker-compose + sudo docker-compose --project-directory docker up -d + + +Notes on the run commands +````````````````````````` + +* ``-v`` is the volume you are mounting ``-v=host_dir:docker_dir`` +* ``pinry/pinry`` is simply what I called my docker build of this image +* ``-d=true`` allows this to run cleanly as a daemon, remove for debugging +* ``-p`` is the port it connects to, ``-p=host_port:docker_port`` +* Follow comments in ``local_settings.py`` to understand how the site configured + +Using docker-pinry +------------------ +Open a browser to ``http://:10000`` and register. Replace YOUR-HOSTNAME with the name +of the machine docker is running on, likely localhost. + +You can map ``http://localhost:10000`` to your outer nginx for SSL or just change +the default port-mapping to ``80:80`` to serve your site directly, just enjoy! + + +Why include nginx and not just map to gunicorn directly? +----------------------------------------------------------- + +Because gunicorn/django can't serve static files very well and it is unwise to do +so for security reasons. I built this so that people can have a full hosted +solution in a container. If you have a host machine running nginx then of course +there is no point to run nginx in the container as well, you can simply disable +nginx, map gunicorn to a port and then set your host machine's nginx to display +your media and static files since that directory is shared between the container +and host. + + +Why use sqlite3? +---------------- + +Because it has a very low resource cost and most pinry websites are small +personal ones. Why have a full on database for that? If you need more power +than you can easily modify the `pinry/local_settings.py` to point to a +stronger database solution. + + +.. Links + +.. _official getting started guide: http://www.docker.io/gettingstarted/ +.. _website: http://getpinry.com/ +.. _additional pinry configuration settings: https://github.com/pinry/pinry/blob/master/docker/pinry/local_settings.example.py diff --git a/docker/bootstrap.sh b/docker/bootstrap.sh new file mode 100644 index 0000000..c5ce0fa --- /dev/null +++ b/docker/bootstrap.sh @@ -0,0 +1,51 @@ +#!/bin/bash + +script_dir="$( dirname "${0}" )" +# Force users to login before seeing any pins. +if [ "${ALLOW_NEW_REGISTRATIONS}" = "" ]; then + ALLOW_NEW_REGISTRATIONS=true +fi + +if [[ "$(docker images -q pinry/pinry 2> /dev/null)" == "" ]]; then + echo "No docker image found, building..." && "${script_dir}/build_docker.sh" +fi + +echo "==================================================================================" +echo "Note: Please copy this key and keep it in a secure place." +echo "Then you should manually edit your pinry/local_settings.py" +echo "and replace SECRET_KEY with new secret-key if you had previously generated a" +echo "pinry/local_settings.py." +echo "If no previous pinry/local_settings.py generated, you can have a look and edit it." +echo "If you want to use docker-compose, just edit docker-compose.yml and use 'docker-compose up'" + +SECRET_KEY=$(sudo docker run pinry/pinry /scripts/gen_key.sh) + +echo "" +echo "Your secret-key is(also saved/overwritten your pinry/production_secret_key.txt):" +echo "" +echo ${SECRET_KEY} +echo "==================================================================================" + +local_settings_file="${script_dir}/pinry/local_settings.py" +# Create local_settings.py +if [ ! -f "${local_settings_file}" ]; +then + cp "${script_dir}/pinry/local_settings.example.py" "${local_settings_file}" + sed -i "s/secret\_key\_place\_holder/${SECRET_KEY}/" "${local_settings_file}" + + # Force users to login before seeing any pins. + if [ "${PRIVATE}" = "true" ]; then + sed -i "s/PUBLIC = True/PUBLIC = False/" "${local_settings_file}" + fi + + # Enable people from creating new accounts. + if [ "${ALLOW_NEW_REGISTRATIONS}" = "true" ]; then + sed -i "s/ALLOW_NEW_REGISTRATIONS = False/ALLOW_NEW_REGISTRATIONS = True/" "${local_settings_file}" + fi +fi + +# Copy to docker-compose.yml +if [ ! -f "${script_dir}/docker-compose.yml" ]; +then + cp "${script_dir}/docker-compose.example.yml" "${script_dir}/docker-compose.yml" +fi diff --git a/docker/build_docker.sh b/docker/build_docker.sh new file mode 100644 index 0000000..f49f92b --- /dev/null +++ b/docker/build_docker.sh @@ -0,0 +1,4 @@ +#!/bin/bash + +script_dir="$( dirname "${0}" )" +sudo docker build -t pinry/pinry${@} "${script_dir}/../" -f "${script_dir}/../Dockerfile.autobuild" diff --git a/docker/docker-compose.example.yml b/docker/docker-compose.example.yml new file mode 100644 index 0000000..61abd05 --- /dev/null +++ b/docker/docker-compose.example.yml @@ -0,0 +1,16 @@ +version: '3' + +services: + web: + build: . + command: > + bash -c "/scripts/start.sh" + ports: +# if you use "127.0.0.1", no one except you can visit it from within +# - "127.0.0.1:10000:8000" + - "80:80" + volumes: + # overwrite local_settings, you can always modify your local_settings file + - ./pinry/local_settings.py:/srv/www/pinry/pinry/settings/local_settings.py + - ./data/:/data/ + restart: always diff --git a/docker/nginx/nginx.conf b/docker/nginx/nginx.conf new file mode 100644 index 0000000..d77653e --- /dev/null +++ b/docker/nginx/nginx.conf @@ -0,0 +1,35 @@ +user www-data; +worker_processes 4; +pid /run/nginx.pid; + +events { + worker_connections 768; +} + +http { + sendfile on; + tcp_nopush on; + tcp_nodelay on; + keepalive_timeout 65; + types_hash_max_size 2048; + client_max_body_size 1m; + + server_names_hash_bucket_size 64; + + include /etc/nginx/mime.types; + default_type application/octet-stream; + + access_log /var/log/nginx/access.log; + error_log /var/log/nginx/error.log; + + gzip on; + gzip_disable "msie6"; + gzip_vary on; + gzip_proxied any; + gzip_comp_level 6; + gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript; + + include /etc/nginx/conf.d/*.conf; + include /etc/nginx/sites-enabled/*; +} + diff --git a/docker/nginx/sites-enabled/default b/docker/nginx/sites-enabled/default new file mode 100644 index 0000000..02180d7 --- /dev/null +++ b/docker/nginx/sites-enabled/default @@ -0,0 +1,26 @@ +server { + listen 80 default; + server_name _; + + access_log /srv/www/pinry/logs/access.log; + error_log /srv/www/pinry/logs/error.log; + + location /media { + alias /data/static/media; + expires max; + access_log off; + } + + location /static { + alias /data/static; + expires max; + access_log off; + } + + location / { + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $remote_addr; + proxy_pass http://localhost:8000; + } +} diff --git a/docker/pinry/.gitignore b/docker/pinry/.gitignore new file mode 100644 index 0000000..49ce140 --- /dev/null +++ b/docker/pinry/.gitignore @@ -0,0 +1,2 @@ +local_settings.py +production_secret_key.txt \ No newline at end of file diff --git a/docker/pinry/local_settings.example.py b/docker/pinry/local_settings.example.py new file mode 100644 index 0000000..d66ba5a --- /dev/null +++ b/docker/pinry/local_settings.example.py @@ -0,0 +1,44 @@ +import os + + +# Please don't change following settings unless you know what you are doing +STATIC_ROOT = '/data/static' + +MEDIA_ROOT = os.path.join(STATIC_ROOT, 'media') + +# SECURITY WARNING: keep the secret key used in production secret! +# Or just write your own secret-key here instead of using a env-variable +SECRET_KEY = "secret_key_place_holder" + +# SECURITY WARNING: don't run with debug turned on in production! +DEBUG = False +TEMPLATE_DEBUG = DEBUG + +# SECURITY WARNING: use your actual domain name in production! +ALLOWED_HOSTS = ['*'] + +# Database +# https://docs.djangoproject.com/en/1.10/ref/settings/#databases + +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.sqlite3', + 'NAME': '/data/production.db', + } +} + +# Allow users to register by themselves +ALLOW_NEW_REGISTRATIONS = False + +# Delete image files once you remove your pin +IMAGE_AUTO_DELETE = True + +# thumbnail size control +IMAGE_SIZES = { + 'thumbnail': {'size': [240, 0]}, + 'standard': {'size': [600, 0]}, + 'square': {'crop': True, 'size': [125, 125]}, +} + +# Whether people can view pins without login +PUBLIC = True diff --git a/docker/scripts/_start_gunicorn.sh b/docker/scripts/_start_gunicorn.sh new file mode 100644 index 0000000..00bd77e --- /dev/null +++ b/docker/scripts/_start_gunicorn.sh @@ -0,0 +1,3 @@ +#!/bin/bash +gunicorn pinry.wsgi -b 0.0.0.0:8000 -w 4 \ + --capture-output --timeout 30 --user www-data --group www-data \ No newline at end of file diff --git a/docker/scripts/gen_key.sh b/docker/scripts/gen_key.sh new file mode 100644 index 0000000..ddc28e0 --- /dev/null +++ b/docker/scripts/gen_key.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +# Check for secret key if one doesn't exist create. +if [ ! -f /data/production_secret_key.txt ] +then + cd /data + PRODUCTION_SECRET_KEY=`pwgen -c -n -1 65` + echo $PRODUCTION_SECRET_KEY > /data/production_secret_key.txt +else + PRODUCTION_SECRET_KEY=`cat /data/production_secret_key.txt` +fi + +echo ${PRODUCTION_SECRET_KEY} diff --git a/docker/scripts/start.sh b/docker/scripts/start.sh new file mode 100644 index 0000000..68251c9 --- /dev/null +++ b/docker/scripts/start.sh @@ -0,0 +1,34 @@ +#!/bin/bash +# ----------------------------------------------------------------------------- +# docker-pinry /start script +# +# Will setup database and static files if they don't exist already, if they do +# just continues to run docker-pinry. +# +# Authors: Isaac Bythewood +# Updated: Aug 19th, 2014 +# ----------------------------------------------------------------------------- + + +# If static files don't exist collect them +if [ ! -d /data/static ] +then + cd /srv/www/pinry + python manage.py collectstatic --noinput +fi + +# If database doesn't exist yet create it +if [ ! -f /data/production.db ] +then + cd /srv/www/pinry + python manage.py migrate --noinput --settings=pinry.settings.docker +fi + +# Fix all settings after all commands are run +chown -R www-data:www-data /data + +# start all process +/usr/sbin/nginx + +cd /srv/www/pinry/ +/scripts/_start_gunicorn.sh diff --git a/docker/start_docker.sh b/docker/start_docker.sh new file mode 100644 index 0000000..c6b1d93 --- /dev/null +++ b/docker/start_docker.sh @@ -0,0 +1,16 @@ +#!/bin/bash + +DATA_PATH=${1} +HERE="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )" +SETTINGS_PATH="${HERE}/pinry/local_settings.py" + +if [ "${DATA_PATH}" = "" ] +then + echo "usage: start_docker.sh /abs/path/to/your/data/store" + exit 1 +fi + +sudo docker run -d=true -p=80:80 \ + -v=${DATA_PATH}:/data \ + -v=${SETTINGS_PATH}:/srv/www/pinry/pinry/settings/local_settings.py \ + pinry/pinry /scripts/start.sh diff --git a/docker/tests/http_status_code.sh b/docker/tests/http_status_code.sh new file mode 100644 index 0000000..6295288 --- /dev/null +++ b/docker/tests/http_status_code.sh @@ -0,0 +1,42 @@ +#!/bin/bash + +set -e + +usage() { + cat <