[
  {
    "path": ".gitignore",
    "content": "./\n../\n.git/\n.idea/\n*.pyc\nflask/htmlcov/\n.coverage"
  },
  {
    "path": ".travis.yml",
    "content": "sudo: required\n\nservices:\n  - docker\n\nenv:\n  DOCKER_COMPOSE_VERSION: 1.13.0\n\nbefore_install:\n  - sudo rm /usr/local/bin/docker-compose\n  - curl -L https://github.com/docker/compose/releases/download/${DOCKER_COMPOSE_VERSION}/docker-compose-`uname -s`-`uname -m` > docker-compose\n  - chmod +x docker-compose\n  - sudo mv docker-compose /usr/local/bin\n\nbefore_script:\n  - docker-compose up --build -d\n  - sleep 10\n\nscript:\n  - docker-compose run flask python manage.py test\n\nafter_script:\n  - docker-compose down"
  },
  {
    "path": "LICENSE",
    "content": "BSD 3-Clause License\n\nCopyright (c) 2017, Radu Raicea\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n\n* Redistributions of source code must retain the above copyright notice, this\n  list of conditions and the following disclaimer.\n\n* Redistributions in binary form must reproduce the above copyright notice,\n  this list of conditions and the following disclaimer in the documentation\n  and/or other materials provided with the distribution.\n\n* Neither the name of the copyright holder nor the names of its\n  contributors may be used to endorse or promote products derived from\n  this software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\nAND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\nIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\nDISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\nFOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\nDAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\nSERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\nCAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\nOR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
  },
  {
    "path": "README.md",
    "content": "<p align=\"center\">\n    <img src=\"dockerized_logo.png\" alt=\"logo\" width=\"800px\">\n</p>\n<p align=\"center\">\n    Dockerized web app template using NGINX, Flask, and PostgreSQL.\n</p>\n<p align=\"center\">\n    <a href=\"https://travis-ci.org/Radu-Raicea/Dockerized-Flask\"><img src=\"https://travis-ci.org/Radu-Raicea/Dockerized-Flask.svg?branch=master\" alt=\"Travis\" /></a>\n</p>\n\n\n    .\n    ├── LICENSE\n    ├── README.md\n    ├── docker-compose.yml\n    ├── dockerized_logo.png\n    ├── flask\n    │   ├── Dockerfile\n    │   ├── config.py\n    │   ├── manage.py\n    │   ├── project\n    │   │   ├── __init__.py\n    │   │   ├── controllers\n    │   │   │   ├── __init__.py\n    │   │   │   └── routes.py\n    │   │   ├── models\n    │   │   │   ├── __init__.py\n    │   │   │   └── names.py\n    │   │   ├── static\n    │   │   │   ├── css\n    │   │   │   ├── img\n    │   │   │   └── js\n    │   │   └── templates\n    │   │       └── index.html\n    │   ├── requirements.txt\n    │   └── tests\n    │       ├── __init__.py\n    │       ├── test_configs.py\n    │       └── test_website.py\n    ├── nginx\n    │   ├── Dockerfile\n    │   ├── app.conf\n    │   └── nginx.conf\n    └── postgres\n        ├── Dockerfile\n        └── create.sql\n\n\n---\n\n\n## Installation\n* [Windows 10 (64-bit Pro)](https://github.com/Radu-Raicea/Dockerized-Flask/wiki/%5BInstallation%5D-Windows-10-Instructions-(64-bit-Pro))\n* [Windows Toolbox](https://github.com/Radu-Raicea/Dockerized-Flask/wiki/%5BInstallation%5D-Windows-Instructions-(Toolbox))\n* [macOS (Yosemite 10.10.3 and higher)](https://github.com/Radu-Raicea/Dockerized-Flask/wiki/%5BInstallation%5D-macOS-Instructions-(Yosemite-10.10.3-and-higher))\n* [Linux (Ubuntu 16.04)](https://github.com/Radu-Raicea/Dockerized-Flask/wiki/%5BInstallation%5D-Linux-Instructions-(Ubuntu-16.04))\n\n## Flask\n* [Using Flask Script to run commands while application is running](https://github.com/Radu-Raicea/Dockerized-Flask/wiki/%5BFlask%5D-Using-Flask-Script-to-run-commands-while-the-application-is-running)\n* [Running unit tests with Flask Testing and coverage](https://github.com/Radu-Raicea/Dockerized-Flask/wiki/%5BFlask%5D-Running-unit-tests-with-Flask-Testing-and-coverage)\n\n## Docker\n* [Remove all Docker volumes to delete the database](https://github.com/Radu-Raicea/Dockerized-Flask/wiki/%5BDocker%5D-Remove-all-Docker-volumes-to-delete-the-database)\n* [Access the PostgreSQL command line terminal through Docker](https://github.com/Radu-Raicea/Dockerized-Flask/wiki/%5BDocker%5D-Access-the-PostgreSQL-command-line-terminal-through-Docker)\n\n## Other\n* [Access the PostgreSQL database using a 3rd party software](https://github.com/Radu-Raicea/Dockerized-Flask/wiki/%5BOther%5D-Access-the-PostgreSQL-database-using-a-3rd-party-software)\n"
  },
  {
    "path": "docker-compose.yml",
    "content": "# --------------------------------------------------------------------------\n# When 'docker-compose up --build' is run, this file is executed.\n#\n# Its purpose is to run 3 containers (nginx, flask and postgres) and\n# attach them together in a common network with shared volumes.\n# --------------------------------------------------------------------------\n\nversion: '3'\n\nservices:\n    postgres:\n      build: ./postgres\n      container_name: postgres\n      ports:\n        - 2345:5432\n      networks:\n        - net\n      environment:\n        - POSTGRES_USER=postgres\n        - POSTGRES_PASSWORD=postgres\n      volumes:\n        - postgres:/var/lib/postgresql/data\n\n    flask:\n      build: ./flask\n      container_name: flask\n      volumes:\n        - ./flask:/usr/src/app\n      networks:\n        - net\n      environment:\n        - APP_SETTINGS=config.DevelopmentConfig\n        - DATABASE_URL=postgresql://postgres:postgres@postgres:5432/db_dev\n        - DATABASE_TEST_URL=postgresql://postgres:postgres@postgres:5432/db_test\n        - SECRET_KEY=dockertutorial\n      depends_on:\n        - postgres\n      links:\n        - postgres\n      command: gunicorn --worker-class eventlet -w 4 -b 0.0.0.0:8000 manage:app\n\n    nginx:\n      build: ./nginx\n      container_name: nginx\n      ports:\n        - 80:80\n      restart: always\n      networks:\n        - net\n      volumes:\n        - ./flask/project/static:/usr/share/nginx/html/static\n      depends_on:\n        - flask\n\nvolumes:\n  postgres:\n\nnetworks:\n  net:"
  },
  {
    "path": "flask/Dockerfile",
    "content": "# --------------------------------------------------------------------------\n# When Docker builds the flask container, it builds it from this image.\n#\n# This file pulls a Python 3 image from Docker Hub (a sort of\n# GitHub for Docker images), and copies the requirements.txt file to the\n# container. It then installs all the Python dependencies from it.\n# --------------------------------------------------------------------------\n\nFROM python:3.6.1\n\nENV PYTHONDONTWRITEBYTECODE=True\n\nRUN mkdir -p /usr/src/app\nWORKDIR /usr/src/app\n\nADD ./requirements.txt /usr/src/app/requirements.txt\n\nRUN pip install -r requirements.txt\n\nADD . /usr/src/app"
  },
  {
    "path": "flask/config.py",
    "content": "# -*- coding: utf-8 -*-\n\n\"\"\"\nThis file stores all the possible configurations for the Flask app.\n\nChanging configurations like the secret key or the database\nurl should be stored as environment variables and imported\nusing the 'os' library in Python.\n\"\"\"\n\nimport os\n\n\nclass BaseConfig:\n    SQLALCHEMY_TRACK_MODIFICATIONS = False\n    SECRET_KEY = os.getenv('SECRET_KEY')\n    DEBUG = False\n    TESTING = False\n\n\nclass TestingConfig(BaseConfig):\n    SQLALCHEMY_DATABASE_URI = os.getenv('DATABASE_TEST_URL')\n    DEBUG = True\n    TESTING = True\n\n\nclass DevelopmentConfig(BaseConfig):\n    SQLALCHEMY_DATABASE_URI = os.getenv('DATABASE_URL')\n    DEBUG = True\n\n\nclass ProductionConfig(BaseConfig):\n    SQLALCHEMY_DATABASE_URI = os.getenv('DATABASE_URL')\n    DEBUG = False\n"
  },
  {
    "path": "flask/manage.py",
    "content": "# -*- coding: utf-8 -*-\n\n\"\"\"\nThis is the entry point of the Flask application.\n\"\"\"\n\nimport unittest\n\nimport coverage\nfrom flask_script import Manager\n\nfrom project import create_app, logger, db\n\n# The logger should always be used instead of a print(). You need to import it from\n# the project package. If you want to understand how to use it properly and why you\n# should use it, check: http://bit.ly/2nqkupO\nlogger.info('Server has started.')\n\n# Defines which parts of the code to include and omit when calculating code coverage.\nCOV = coverage.coverage(\n    branch=True,\n    include='project/*',\n    omit=[\n        'tests/*',\n        'project/website/*'\n    ]\n)\nCOV.start()\n\n# Creates the Flask application object that we use to initialize things in the app.\napp = create_app()\n\n# Creates all the models specified in project/models\nimport project.models\ndb.create_all(app=app)\n\n# Initializes the Manager object, which allows us to run terminal commands on the\n# Flask application while it's running (using Flask-Script).\nmanager = Manager(app)\n\n\n@manager.command\ndef cov():\n    \"\"\"\n    Runs the unit tests and generates a coverage report on success.\n\n    While the application is running, you can run the following command in a new terminal:\n    'docker-compose run --rm flask python manage.py cov' to run all the tests in the\n    'tests' directory. If all the tests pass, it will generate a coverage report.\n\n    :return int: 0 if all tests pass, 1 if not\n    \"\"\"\n\n    tests = unittest.TestLoader().discover('tests')\n    result = unittest.TextTestRunner(verbosity=2).run(tests)\n    if result.wasSuccessful():\n        COV.stop()\n        COV.save()\n        print('Coverage Summary:')\n        COV.report()\n        COV.html_report()\n        COV.erase()\n        return 0\n    else:\n        return 1\n\n\n@manager.command\ndef test():\n    \"\"\"\n    Runs the unit tests without generating a coverage report.\n\n    Enter 'docker-compose run --rm flask python manage.py test' to run all the tests in the\n    'tests' directory, with no coverage report.\n\n    :return int: 0 if all tests pass, 1 if not\n    \"\"\"\n\n    tests = unittest.TestLoader().discover('tests', pattern='test*.py')\n    result = unittest.TextTestRunner(verbosity=2).run(tests)\n    if result.wasSuccessful():\n        return 0\n    else:\n        return 1\n\n\n@manager.command\ndef test_one(test_file):\n    \"\"\"\n    Runs the unittest without generating a coverage report.\n\n    Enter 'docker-compose run --rm flask python manage.py test_one <NAME_OF_FILE>' to run only\n    one test file in the 'tests' directory. It provides no coverage report.\n\n    Example: 'docker-compose run --rm flask python manage.py test_one test_website'\n    Note that you do not need to put the extension of the test file.\n\n    :return int: 0 if all tests pass, 1 if not\n    \"\"\"\n\n    tests = unittest.TestLoader().discover('tests', pattern=test_file + '.py')\n    result = unittest.TextTestRunner(verbosity=2).run(tests)\n    if result.wasSuccessful():\n        return 0\n    else:\n        return 1\n\n# Starts the Flask app.\nif __name__ == '__main__':\n    manager.run()\n"
  },
  {
    "path": "flask/project/__init__.py",
    "content": "# -*- coding: utf-8 -*-\n\n\"\"\"\nThis is the root of the main package of our Flask app: project.\n\nWhenever you see 'from project import <something>', it takes it\nfrom here.\n\"\"\"\n\nimport os\nimport logging\n\nfrom flask import Flask\nfrom flask_sqlalchemy import SQLAlchemy\n\n# Defines the format of the logging to include the time and to use the INFO logging level or worse.\nlogging.basicConfig(format='%(asctime)s %(levelname)s: %(message)s', datefmt='%Y-%m-%d %H:%M:%S', level=logging.INFO)\nlogger = logging.getLogger(__name__)\n\ndb = SQLAlchemy()\n\n\ndef create_app():\n    \"\"\"\n    Flask application factory that creates app instances.\n\n    Every time this function is called, a new application instance is created. The reason\n    why an application factory is needed is because we need to use different configurations\n    for running our tests.\n\n    :return Flask object: Returns a Flask application instance\n    \"\"\"\n\n    app = Flask(__name__)\n    app_settings = os.getenv('APP_SETTINGS')\n    app.config.from_object(app_settings)\n\n    db.init_app(app)\n\n    # Blueprints are used for scalability. If you want to read more about it, visit:\n    # http://flask.pocoo.org/docs/0.12/blueprints/\n    from project.controllers.routes import website_blueprint\n    app.register_blueprint(website_blueprint)\n\n    return app\n"
  },
  {
    "path": "flask/project/controllers/__init__.py",
    "content": "# -*- coding: utf-8 -*-\n\n\"\"\"\nWhenever you want to create a package in Python, you must create a directory\nwith an __init__.py inside, even if it's empty.\n\"\"\"\n"
  },
  {
    "path": "flask/project/controllers/routes.py",
    "content": "# -*- coding: utf-8 -*-\n\n\"\"\"\nThis is where all the routes and controllers are defined.\n\"\"\"\n\nfrom flask import render_template, Blueprint\n\nwebsite_blueprint = Blueprint('website_blueprint', __name__)\n\n@website_blueprint.route('/')\ndef index():\n    # Controller logic should go here\n\treturn render_template('index.html')\n"
  },
  {
    "path": "flask/project/models/__init__.py",
    "content": "# -*- coding: utf-8 -*-\n\n\"\"\"\nWhenever you want to create a package in Python, you must create a directory\nwith an __init__.py inside, even if it's empty.\n\"\"\"\n"
  },
  {
    "path": "flask/project/models/names.py",
    "content": "# -*- coding: utf-8 -*-\n\nfrom flask_sqlalchemy import SQLAlchemy\n\nfrom project import db\n\n\nclass Name(db.Model):\n\n    id = db.Column(db.Integer, primary_key=True)\n    first_name = db.Column(db.String(128))\n    last_name = db.Column(db.String(128))\n\n    def __init__(self, first_name, last_name):\n        self.first_name = first_name\n        self.last_name = last_name\n\n    def __repr__(self):\n        return '<Name Object %r>' % self.id\n"
  },
  {
    "path": "flask/project/static/css/style.css",
    "content": "body {\n    margin: 10px;\n}"
  },
  {
    "path": "flask/project/static/js/script.js",
    "content": "// js"
  },
  {
    "path": "flask/project/templates/index.html",
    "content": "<!DOCTYPE html>\n<html>\n    <head>\n        <meta charset=\"UTF-8\">\n        <title>Template Flask App</title>\n        <link rel=\"stylesheet\" type=\"text/css\" href=\"/static/css/style.css\">\n    </head>\n\n    <body>\n        <center>\n            <h1>It's Working!</h1>\n            <a href=\"/static/img/nya.gif\"><b>NYA</b></a>\n        </center>\n    </body>\n</html>"
  },
  {
    "path": "flask/requirements.txt",
    "content": "flask==0.12.2\ngunicorn==19.7.1\npsycopg2==2.7.1\nFlask-SQLAlchemy==2.2\neventlet==0.21.0\nflask-script==2.0.5\ncoverage==4.4.1\nflask_testing==0.6.2"
  },
  {
    "path": "flask/tests/__init__.py",
    "content": "# -*- coding: utf-8 -*-\n\n\"\"\"\nWhenever you want to create a package in Python, you must create a directory\nwith an __init__.py inside, even if it's empty.\n\"\"\"\n"
  },
  {
    "path": "flask/tests/test_configs.py",
    "content": "# -*- coding: utf-8 -*-\n\n\"\"\"\nThis file tests the various configurations of the Flask app.\n\nIt's pretty standard and shouldn't really be modified, unless you add\nnew configurations.\n\"\"\"\n\nimport os\nimport unittest\n\nfrom flask import current_app\nfrom flask_testing import TestCase\n\nfrom project import create_app\n\napp = create_app()\n\n\nclass TestDevelopmentConfig(TestCase):\n\n    def create_app(self):\n        app.config.from_object('config.DevelopmentConfig')\n        return app\n\n    def test_app_is_development(self):\n        self.assertTrue(app.config['SECRET_KEY'] == os.getenv('SECRET_KEY'))\n        self.assertTrue(app.config['DEBUG'] is True)\n        self.assertFalse(current_app is None)\n        self.assertTrue(app.config['SQLALCHEMY_DATABASE_URI'] == os.environ.get('DATABASE_URL'))\n\n\nclass TestTestingConfig(TestCase):\n\n    def create_app(self):\n        app.config.from_object('config.TestingConfig')\n        return app\n\n    def test_app_is_testing(self):\n        self.assertTrue(app.config['SECRET_KEY'] == os.getenv('SECRET_KEY'))\n        self.assertTrue(app.config['DEBUG'])\n        self.assertTrue(app.config['TESTING'])\n        self.assertFalse(app.config['PRESERVE_CONTEXT_ON_EXCEPTION'])\n        self.assertTrue(app.config['SQLALCHEMY_DATABASE_URI'] == os.environ.get('DATABASE_TEST_URL'))\n\n\nclass TestProductionConfig(TestCase):\n\n    def create_app(self):\n        app.config.from_object('config.ProductionConfig')\n        return app\n\n    def test_app_is_production(self):\n        self.assertTrue(app.config['SECRET_KEY'] == os.getenv('SECRET_KEY'))\n        self.assertFalse(app.config['DEBUG'])\n        self.assertFalse(app.config['TESTING'])\n\nif __name__ == '__main__':\n    unittest.main()\n"
  },
  {
    "path": "flask/tests/test_website.py",
    "content": "# -*- coding: utf-8 -*-\n\n\"\"\"\nThis file defines the group of tests for the simple website routes.\n\nYou can run this test group file by running the application and\nrunning 'docker-compose run --rm flask python manage.py test_one test_website'\nin a separate terminal window.\n\"\"\"\n\nimport logging\nimport os\nimport unittest\n\nfrom flask_testing import TestCase\n\nfrom project import create_app, logger\n\n# Creates a new instance of the Flask application. The reason for this\n# is that we can't interrupt the application instance that is currently\n# running and serving requests.\napp = create_app()\n\n\nclass TestWebsite(TestCase):\n\n    def create_app(self):\n        \"\"\"\n        Instructs Flask to run these commands when we request this group of tests to be run.\n        \"\"\"\n        \n        # Sets the configuration of the application to 'TestingConfig' in order\n        # that the tests use db_test, not db_dev or db_prod.\n        app.config.from_object('config.TestingConfig')\n\n        # Sets the logger to only show ERROR level logs and worse. We don't want\n        # to print a million things when running tests.\n        logger.setLevel(logging.ERROR)\n\n        return app\n\n    def setUp(self):\n        \"\"\"Defines what should be done before every single test in this test group.\"\"\"\n        pass\n\n    def tearDown(self):\n        \"\"\"Defines what should be done after every single test in this test group.\"\"\"\n        pass\n\n    def test_index_page_successful(self):\n        \"\"\"\n        Every single test in this test group should be defined as a method of this class.\n\n        The methods should be named as follows: test_<name_of_test>\n        \"\"\"\n        \n        with self.client:\n            response = self.client.get('/')\n            self.assertEqual(response.status_code, 200)\n\n# Runs the tests.\nif __name__ == '__main__':\n    unittest.main()\n"
  },
  {
    "path": "nginx/Dockerfile",
    "content": "# --------------------------------------------------------------------------\n# When Docker builds the nginx container, it builds it from this image.\n#\n# This file pulls a nginx image from Docker Hub (a sort of\n# GitHub for Docker images), and copies NGINX config files to the container.\n# --------------------------------------------------------------------------\n\nFROM nginx:1.13.0\n\nRUN rm /etc/nginx/nginx.conf\nCOPY nginx.conf /etc/nginx/\nRUN rm /etc/nginx/conf.d/default.conf\nCOPY app.conf /etc/nginx/conf.d/"
  },
  {
    "path": "nginx/app.conf",
    "content": "server {\n    listen 80;\n    \n    charset utf-8;\n\n    location /static/ {\n        autoindex on;\n        root /usr/share/nginx/html/;\n    }\n\n    location / {\n        proxy_pass http://flask:8000;\n        proxy_set_header Host $host;\n        proxy_set_header X-Real-IP $remote_addr;\n        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n    }\n}"
  },
  {
    "path": "nginx/nginx.conf",
    "content": "user  nginx;\nworker_processes  1;\n\nerror_log  /var/log/nginx/error.log warn;\npid        /var/run/nginx.pid;\n\nevents {\n    worker_connections  1024;\n}\n\nhttp {\n    include       /etc/nginx/mime.types;\n    default_type  text/html;\n\n    log_format  main  '$remote_addr - $remote_user [$time_local] \"$request\" '\n                      '$status $body_bytes_sent \"$http_referer\" '\n                      '\"$http_user_agent\" \"$http_x_forwarded_for\"';\n\n    access_log  /var/log/nginx/access.log  main;\n\n    sendfile       on;\n    tcp_nopush     on;\n    tcp_nodelay    on;\n\n    keepalive_timeout  65;\n\n    include /etc/nginx/conf.d/*.conf;\n}\n"
  },
  {
    "path": "postgres/Dockerfile",
    "content": "# -----------------------------------------------------------------------\n# When Docker builds the postgres container, it builds it from this image.\n#\n# This file pulls the latest postgres image from Docker Hub (a sort of\n# GitHub for Docker images), and runs the create.sql file.\n# -----------------------------------------------------------------------\n\nFROM postgres\n\nADD create.sql /docker-entrypoint-initdb.d"
  },
  {
    "path": "postgres/create.sql",
    "content": "-- -----------------------------------------------------------------------\n-- This file is run when the postgres container is first created. It\n-- creates 3 databases, one for each configuration.\n-- -----------------------------------------------------------------------\n\nCREATE DATABASE db_dev;\nCREATE DATABASE db_prod;\nCREATE DATABASE db_test;"
  }
]