Skip to content

Commit

Permalink
Merge pull request #8 from jecklgamis/next
Browse files Browse the repository at this point in the history
Use Ubuntu 24.04
  • Loading branch information
jecklgamis authored Jun 17, 2024
2 parents 1f35e7a + ec3222a commit 0dc5b68
Show file tree
Hide file tree
Showing 14 changed files with 89 additions and 108 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,10 @@ jobs:
python -m pip install --upgrade pip
pip install pytest
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
- name: Build server_info, ssl certs
- name: Build build info, ssl certs
run: |
./generate-ssl-certs.sh
./generate-server-info.sh
./generate-build-info.sh
- name: Test with pytest
run: |
pytest
Expand Down
21 changes: 9 additions & 12 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,27 +1,24 @@
FROM ubuntu:22.04
FROM ubuntu:24.04
MAINTAINER Jerrico Gamis <[email protected]>

RUN apt update -y && apt install -y curl dumb-init python3 python3-pip && rm -rf /var/lib/apt/lists/*
RUN pip3 install --upgrade pip
RUN apt update -y && apt install -y curl dumb-init python3 python3-venv python3-pip && rm -rf /var/lib/apt/lists/*
RUN python3 -m venv /python3env

COPY requirements.txt /
RUN pip3 install --trusted-host pypi.python.org -r /requirements.txt
RUN /python3env/bin/pip3 install --trusted-host pypi.python.org -r /requirements.txt

ENV APP_HOME /app

COPY app/* ${APP_HOME}/app/
COPY server_info.json ${APP_HOME}

COPY server.crt ${APP_HOME}
COPY server.key ${APP_HOME}
COPY app/* $APP_HOME/app/
COPY build-info.json $APP_HOME
COPY server.crt $APP_HOME
COPY server.key $APP_HOME

EXPOSE 8080
EXPOSE 8443

WORKDIR ${APP_HOME}
WORKDIR $APP_HOME

COPY docker-entrypoint.sh /
RUN chmod +x /docker-entrypoint.sh

ENTRYPOINT ["/usr/bin/dumb-init", "--"]
CMD ["/docker-entrypoint.sh"]
44 changes: 21 additions & 23 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,31 +1,29 @@
IMAGE_NAME:=jecklgamis/flask-app-example
IMAGE_TAG:=main
IMAGE_NAME:=flask-app-example
IMAGE_TAG:=$(shell git rev-parse --abbrev-ref HEAD)

default:
echo $(IMAGE_TAG)
cat ./Makefile
dist:
@cat ./Makefile
install-deps:
@pip3 install -r requirements.txt
build:
@./generate-ssl-certs.sh
@./generate-server-info.sh
@./generate-build-info.sh
image:
docker build -t $(IMAGE_NAME):$(IMAGE_TAG) .
run-shell:
@docker run -i -t $(IMAGE_NAME):$(IMAGE_TAG) /bin/bash
run:
@docker run -p 8443:8443 -p 8080:8080 -it $(IMAGE_NAME):$(IMAGE_TAG)

all : dist tests image
@docker run -p 8080:8080 -p 8443:8443 -it $(IMAGE_NAME):$(IMAGE_TAG)
run-shell:
@docker run -it $(IMAGE_NAME):$(IMAGE_TAG) /bin/bash
exec-shell:
docker exec -it `docker ps | grep $(IMAGE_NAME) | awk '{print $$1}'` /bin/bash
all: build check image
up: all run

install-deps:
@pip3 install -r requirements.txt
@pip3 install -r requirements-dev.txt
run-app-dev-mode:
@./run-app-dev-mode.sh
run-app-dev-mode-ssl:
@./run-app-dev-mode.sh ssl
smoke-tests:
run-smoke-tests:
@./smoke-tests.py
.PHONY: tests
tests:
pytest -s
check:
@pytest -s
clean:
@find . -name build-info.json | xargs rm -f
@find . -name server.key | xargs rm -f
@find . -name server.crt | xargs rm -f
@find . -name *.log| xargs rm -f
44 changes: 24 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,35 +2,38 @@

[![Build](https://github.com/jecklgamis/flask-app-example/actions/workflows/build.yml/badge.svg)](https://github.com/jecklgamis/flask-app-example/actions/workflows/build.yml)

An example Flask app using Python 3 and Docker.
An example Flask app.

Docker : `docker run -p 8080:8080 -it jecklgamis/flask-app-example:main`

What's in the box?
## What's In The Box?

* Ubuntu Linux based Docker image
* SSL/TLS listener
* Modular route handlers using [Flask Blueprints](https://flask.palletsprojects.com/en/1.1.x/blueprints/)
* Ubuntu Docker image
* [Gunicorn](https://gunicorn.org) WSGI server
* Flask tests (under `tests` directory) using `pytest` and a `smoke-tests.py` for basic endpoint testing
* Build info, liveness and readiness probes
* PyTest unit tests
* HTTP/HTTPS listeners

## Preparing Your Environment
* Ensure [Python 3](https://www.python.org/downloads/), [Docker](https://www.docker.com/), and
[GNU Make](https://www.gnu.org/software/make/) are installed
## Requirements

Install Python dependencies:
```
make install-deps
```
## Building
To build the app:
* Python 3
* Docker
* Make

## Building

Run `make install-deps` or `pip install -r requirements.txt` to install Python dependencies

## Building

Build Docker image
```
make all
```
This does a couple of things:
* It generates self-signed SSL certificates (`server.key` and `server.crt`)
* It generates `server_info.json` that is served by the `/server_info` endpoint
* It runs tests using `pytest`
* It generates `build-info.json` that is served by the `/build-info` endpoint
* It runs tests
* It generates a Docker image

Explore the `Makefile` for details.
Expand All @@ -42,9 +45,9 @@ make run
```

To run the app directly without using Docker:

```
make run-app-dev-mode
make run-app-dev-mode-ssl
flask run --host 0.0.0.0 --port 8080
```

## Testing The EndPoints
Expand All @@ -53,6 +56,7 @@ make smoke-tests
```

## Contributing
Please raise issue or pull request. Thanks for contributing!

Please raise issue or pull request.

Have fun!
8 changes: 4 additions & 4 deletions app/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,16 @@ def configure_logging():
def create_app():
configure_logging()
app = Flask(__name__, instance_relative_config=True)
app.config.from_mapping(
SECRET_KEY=os.urandom(16)
)
app.config.from_mapping(SECRET_KEY=os.urandom(16))
app.config.from_pyfile('config.py', silent=True)
try:
os.makedirs(app.instance_path)
except OSError as e:
pass

app.register_blueprint(api.bp)
app.register_blueprint(root.bp)
app.register_blueprint(probe.bp)
return app


app = create_app()
6 changes: 3 additions & 3 deletions app/root.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ def index():
return jsonify({"name": "flask-app-example", "message": "It works on my machine!"})


@bp.route('/server_info', methods=['GET'])
def server_info():
info = json.loads(io.open('server_info.json').read())
@bp.route('/build-info', methods=['GET'])
def build_info():
info = json.loads(io.open('build-info.json').read())
return jsonify(info)
9 changes: 7 additions & 2 deletions docker-entrypoint.sh
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
#!/usr/bin/env bash
# exec gunicorn --bind 0.0.0.0:8443 --certfile server.crt --keyfile server.key 'app:create_app()'
exec gunicorn --bind 0.0.0.0:8080 'app:create_app()'
source /python3env/bin/activate

if [ "$USE_SSL" = "true" ]; then
exec gunicorn --bind 0.0.0.0:8443 --certfile server.crt --keyfile server.key 'app:app'
else
exec gunicorn --bind 0.0.0.0:8080 'app:app'
fi
4 changes: 2 additions & 2 deletions generate-server-info.sh → generate-build-info.sh
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#!/bin/bash
BRANCH=$(git rev-parse --abbrev-ref HEAD)
COMMIT_ID=$(git rev-parse HEAD)
echo "{ \"version\":\"${COMMIT_ID}\", \"branch\":\"${BRANCH}\", \"name\":\"flask-app-example\"}" > server_info.json
echo "Wrote server_info.json"
echo "{ \"version\":\"${COMMIT_ID}\", \"branch\":\"${BRANCH}\", \"name\":\"flask-app-example\"}" > build-info.json
echo "Wrote build-info.json"
2 changes: 1 addition & 1 deletion generate-ssl-certs.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ SERVER_KEY=server.key
SERVER_CERT=server.crt
rm -f ${SERVER_KEY}
rm -f ${SERVER_CERT}
SUBJECT="/C=CC/ST=State/L=Locatlity/O=Org/OU=OrgUnit/CN=flask-app-example" > /dev/null
SUBJECT="/C=CC/ST=State/L=Locality/O=Org/OU=OrgUnit/CN=flask-app-example" > /dev/null
openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout ${SERVER_KEY} -out ${SERVER_CERT} -subj ${SUBJECT} > /dev/null
openssl x509 -in ${SERVER_CERT} -text -noout > /dev/null
echo "Wrote ${SERVER_KEY}"
Expand Down
2 changes: 0 additions & 2 deletions requirements-dev.txt

This file was deleted.

7 changes: 4 additions & 3 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
Flask==3.0.0
gunicorn==21.2.0
requests==2.31.0
Flask==3.0.3
gunicorn==22.0.0
requests==2.32.3
pytest==8.2.2
6 changes: 2 additions & 4 deletions run-app-dev-mode.sh → run-app.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,10 @@
USE_SSL=$1

export FLASK_APP=app
export FLASK_ENV=development
export FLASK_DEBUG=true

if [ ! -z "${USE_SSL}" ]; then
echo "Starting app using SSL"
if [ "$USE_SSL" = "true" ]; then
flask run --cert server.crt --key server.key --host 0.0.0.0 --port 8443
else
echo "Starting app"
flask run --host 0.0.0.0 --port 8080
fi
36 changes: 8 additions & 28 deletions smoke-tests.py
Original file line number Diff line number Diff line change
@@ -1,36 +1,16 @@
#!/usr/bin/env python3

import logging as log
import sys
import unittest

import requests
import urllib3


class SmokeTests(unittest.TestCase):

def setUp(self) -> None:
SmokeTests.init()

def testEndPointsExist(self):
log.info("Ensuring endpoints exist")
self.assertEqual(SmokeTests.get("https://localhost:8443/").status_code, 200)
self.assertEqual(SmokeTests.get("https://localhost:8443/server_info").status_code, 200)
self.assertEqual(SmokeTests.get("https://localhost:8443/api").status_code, 200)

@staticmethod
def get(url):
return requests.get(url, verify=False)

@staticmethod
def init():
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
stdout_handler = log.StreamHandler(sys.stdout)
log.basicConfig(level=log.INFO,
format='[%(asctime)s] {%(filename)s:%(lineno)d} %(levelname)s - %(message)s',
handlers=[stdout_handler])
def test_endpoint_exists():
assert requests.get("http://localhost:8080/").status_code == 200
assert requests.get("http://localhost:8080/build-info").status_code == 200


if __name__ == '__main__':
unittest.main()
try:
test_endpoint_exists()
print("OK")
except:
print("NG")
4 changes: 2 additions & 2 deletions tests/test_root.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ def test_index(client):
assert "It works on my machine!" == data["message"]


def test_server_info(client):
response = client.get("/server_info")
def test_build_info(client):
response = client.get("/build-info")
data = response.get_json()
assert "flask-app-example" == data["name"]
assert data["version"] is not None

0 comments on commit 0dc5b68

Please sign in to comment.