Skip to content

Commit

Permalink
part 1
Browse files Browse the repository at this point in the history
  • Loading branch information
mjhea0 committed Mar 17, 2019
1 parent 879ae14 commit 0b5d587
Show file tree
Hide file tree
Showing 25 changed files with 714 additions and 18 deletions.
37 changes: 37 additions & 0 deletions docker-compose-prod.yml
@@ -0,0 +1,37 @@
version: '3.7'

services:

users:
build:
context: ./services/users
dockerfile: Dockerfile-prod
expose:
- 5000
environment:
- FLASK_ENV=production
- APP_SETTINGS=project.config.ProductionConfig
- DATABASE_URL=postgres://postgres:postgres@users-db:5432/users_prod
- DATABASE_TEST_URL=postgres://postgres:postgres@users-db:5432/users_test
depends_on:
- users-db

users-db:
build:
context: ./services/users/project/db
dockerfile: Dockerfile
expose:
- 5432
environment:
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=postgres

nginx:
build:
context: ./services/nginx
dockerfile: Dockerfile-prod
restart: always
ports:
- 80:80
depends_on:
- users
39 changes: 39 additions & 0 deletions docker-compose.yml
@@ -0,0 +1,39 @@
version: '3.7'

services:

users:
build:
context: ./services/users
dockerfile: Dockerfile
volumes:
- './services/users:/usr/src/app'
ports:
- 5001:5000
environment:
- FLASK_ENV=development
- APP_SETTINGS=project.config.DevelopmentConfig
- DATABASE_URL=postgres://postgres:postgres@users-db:5432/users_dev
- DATABASE_TEST_URL=postgres://postgres:postgres@users-db:5432/users_test
depends_on:
- users-db

users-db:
build:
context: ./services/users/project/db
dockerfile: Dockerfile
ports:
- 5436:5432
environment:
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=postgres

nginx:
build:
context: ./services/nginx
dockerfile: Dockerfile
restart: always
ports:
- 80:80
depends_on:
- users
4 changes: 4 additions & 0 deletions services/nginx/Dockerfile
@@ -0,0 +1,4 @@
FROM nginx:1.15.9-alpine

RUN rm /etc/nginx/conf.d/default.conf
COPY /dev.conf /etc/nginx/conf.d
4 changes: 4 additions & 0 deletions services/nginx/Dockerfile-prod
@@ -0,0 +1,4 @@
FROM nginx:1.15.9-alpine

RUN rm /etc/nginx/conf.d/default.conf
COPY /prod.conf /etc/nginx/conf.d
14 changes: 14 additions & 0 deletions services/nginx/dev.conf
@@ -0,0 +1,14 @@
server {

listen 80;

location / {
proxy_pass http://users:5000;
proxy_redirect default;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Host $server_name;
}

}
14 changes: 14 additions & 0 deletions services/nginx/prod.conf
@@ -0,0 +1,14 @@
server {

listen 80;

location / {
proxy_pass http://users:5000;
proxy_redirect default;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Host $server_name;
}

}
4 changes: 4 additions & 0 deletions services/users/.dockerignore
@@ -0,0 +1,4 @@
env
.dockerignore
Dockerfile-dev
Dockerfile-prod
25 changes: 25 additions & 0 deletions services/users/Dockerfile
@@ -0,0 +1,25 @@
# base image
FROM python:3.7.2-alpine

# install dependencies
RUN apk update && \
apk add --virtual build-deps gcc python-dev musl-dev && \
apk add postgresql-dev && \
apk add netcat-openbsd

# set working directory
WORKDIR /usr/src/app

# add and install requirements
COPY ./requirements.txt /usr/src/app/requirements.txt
RUN pip install -r requirements.txt

# add entrypoint.sh
COPY ./entrypoint.sh /usr/src/app/entrypoint.sh
RUN chmod +x /usr/src/app/entrypoint.sh

# add app
COPY . /usr/src/app

# run server
CMD ["/usr/src/app/entrypoint.sh"]
25 changes: 25 additions & 0 deletions services/users/Dockerfile-prod
@@ -0,0 +1,25 @@
# base image
FROM python:3.7.2-alpine

# install dependencies
RUN apk update && \
apk add --virtual build-deps gcc python-dev musl-dev && \
apk add postgresql-dev && \
apk add netcat-openbsd

# set working directory
WORKDIR /usr/src/app

# add and install requirements
COPY ./requirements.txt /usr/src/app/requirements.txt
RUN pip install -r requirements.txt

# add entrypoint.sh
COPY ./entrypoint.sh /usr/src/app/entrypoint-prod.sh
RUN chmod +x /usr/src/app/entrypoint-prod.sh

# add app
COPY . /usr/src/app

# run server
CMD ["/usr/src/app/entrypoint-prod.sh"]
11 changes: 11 additions & 0 deletions services/users/entrypoint-prod.sh
@@ -0,0 +1,11 @@
#!/bin/sh

echo "Waiting for postgres..."

while ! nc -z users-db 5432; do
sleep 0.1
done

echo "PostgreSQL started"

gunicorn -b 0.0.0.0:5000 manage:app
11 changes: 11 additions & 0 deletions services/users/entrypoint.sh
@@ -0,0 +1,11 @@
#!/bin/sh

echo "Waiting for postgres..."

while ! nc -z users-db 5432; do
sleep 0.1
done

echo "PostgreSQL started"

python manage.py run -h 0.0.0.0
33 changes: 31 additions & 2 deletions services/users/manage.py
@@ -1,12 +1,41 @@
# services/users/manage.py


import sys
import unittest

from flask.cli import FlaskGroup

from project import app
from project import create_app, db
from project.api.models import User

app = create_app()
cli = FlaskGroup(create_app=create_app)


@cli.command('recreate_db')
def recreate_db():
db.drop_all()
db.create_all()
db.session.commit()


@cli.command('seed_db')
def seed_db():
"""Seeds the database."""
db.session.add(User(username='michael', email="hermanmu@gmail.com"))
db.session.add(User(username='michaelherman', email="michael@mherman.org"))
db.session.commit()


cli = FlaskGroup(app)
@cli.command()
def test():
"""Runs the tests without code coverage"""
tests = unittest.TestLoader().discover('project/tests', pattern='test*.py')
result = unittest.TextTestRunner(verbosity=2).run(tests)
if result.wasSuccessful():
return 0
sys.exit(result)


if __name__ == '__main__':
Expand Down
38 changes: 24 additions & 14 deletions services/users/project/__init__.py
@@ -1,25 +1,35 @@
# services/users/project/__init__.py


from flask import Flask, jsonify
from flask_restful import Resource, Api
import os

from flask import Flask
from flask_sqlalchemy import SQLAlchemy

# instantiate the app
app = Flask(__name__)

api = Api(app)
# instantiate the db
db = SQLAlchemy()

# set config
app.config.from_object('project.config.DevelopmentConfig') # new

def create_app(script_info=None):

class UsersPing(Resource):
def get(self):
return {
'status': 'success',
'message': 'pong!'
}
# instantiate the app
app = Flask(__name__)

# set config
app_settings = os.getenv('APP_SETTINGS')
app.config.from_object(app_settings)

api.add_resource(UsersPing, '/users/ping')
# set up extensions
db.init_app(app)

# register blueprints
from project.api.users import users_blueprint
app.register_blueprint(users_blueprint)

# shell context for flask cli
@app.shell_context_processor
def ctx():
return {'app': app, 'db': db}

return app
1 change: 1 addition & 0 deletions services/users/project/api/__init__.py
@@ -0,0 +1 @@
# services/users/project/api/__init__.py
29 changes: 29 additions & 0 deletions services/users/project/api/models.py
@@ -0,0 +1,29 @@
# services/users/project/api/models.py


from sqlalchemy.sql import func

from project import db


class User(db.Model):

__tablename__ = 'users'

id = db.Column(db.Integer, primary_key=True, autoincrement=True)
username = db.Column(db.String(128), nullable=False)
email = db.Column(db.String(128), nullable=False)
active = db.Column(db.Boolean(), default=True, nullable=False)
created_date = db.Column(db.DateTime, default=func.now(), nullable=False)

def __init__(self, username, email):
self.username = username
self.email = email

def to_json(self):
return {
'id': self.id,
'username': self.username,
'email': self.email,
'active': self.active
}
55 changes: 55 additions & 0 deletions services/users/project/api/templates/index.html
@@ -0,0 +1,55 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Flask on Docker</title>
<!-- meta -->
<meta name="description" content="">
<meta name="author" content="">
<meta name="viewport" content="width=device-width,initial-scale=1">
<!-- styles -->
<link
href="//cdnjs.cloudflare.com/ajax/libs/bulma/0.7.4/css/bulma.min.css"
rel="stylesheet"
>
{% block css %}{% endblock %}
</head>
<body>
<div class="container">
<div class="column is-one-third">
<br>
<h1 class="title">All Users</h1>
<hr><br>
<form action="/" method="POST">
<div class="field">
<input
name="username" class="input"
type="text" placeholder="Enter a username" required>
</div>
<div class="field">
<input
name="email" class="input"
type="email" placeholder="Enter an email address" required>
</div>
<input
type="submit" class="button is-primary is-fullwidth"
value="Submit">
</form>
<br>
<hr>
{% if users %}
<ol>
{% for user in users %}
<li>{{user.username}}</li>
{% endfor %}
</ol>
{% else %}
<p>No users!</p>
{% endif %}
</div>
</div>
</div>
</script>
{% block js %}{% endblock %}
</body>
</html>

0 comments on commit 0b5d587

Please sign in to comment.