From baed30754e9d1456539aea517fb304cf73f1cd08 Mon Sep 17 00:00:00 2001 From: danijoo Date: Sun, 27 Nov 2022 10:40:20 +0100 Subject: [PATCH 1/4] initial commit --- .gitignore | 2 ++ Dockerfile | 37 +++++++++++++++++++++++++++++++++++++ app.py | 12 ++++++++++++ requirements.txt | 1 + 4 files changed, 52 insertions(+) create mode 100644 .gitignore create mode 100644 Dockerfile create mode 100644 app.py create mode 100644 requirements.txt diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..82195aa --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +venv +.idea \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..8594b1c --- /dev/null +++ b/Dockerfile @@ -0,0 +1,37 @@ +# build pdfact +FROM ubuntu:20.04 +ENV DEBIAN_FRONTEND=noninteractive +RUN apt-get update -y && apt-get upgrade -y && apt-get install -y maven git + +WORKDIR / +RUN git clone https://github.com/ad-freiburg/pdfact.git +WORKDIR pdfact + +RUN mvn install -DskipTests + +FROM alpine:latest +WORKDIR / + +# copy build artifact +COPY --from=0 /pdfact/bin/pdfact.jar pdfact.jar + +# get java +RUN apk update \ + && apk upgrade \ + && apk add --no-cache openjdk11-jre +ENV JAVA_HOME="/usr/lib/jvm/java-11-openjdk" + +# get python +RUN apk add --no-cache python3 \ + && python3 -m ensurepip \ + && pip3 install --upgrade pip setuptools + + +# install flask app +WORKDIR /app +COPY ./requirements.txt . +RUN pip install -r requirements.txt + +COPY app.py . +ENTRYPOINT ["python3"] +CMD ["app.py"] diff --git a/app.py b/app.py new file mode 100644 index 0000000..79c4aa6 --- /dev/null +++ b/app.py @@ -0,0 +1,12 @@ +from flask import Flask + +app = Flask(__name__) + + +@app.route('/', methods=["GET"]) +def hello_world(): # put application's code here + return 'Hello World!' + + +if __name__ == '__main__': + app.run(host="0.0.0.0", port=5000) diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..3c78d8f --- /dev/null +++ b/requirements.txt @@ -0,0 +1 @@ +Flask==2.2.2 \ No newline at end of file From 233c14ec2d9d72d8801a59231c7ca7f5ccb0f745 Mon Sep 17 00:00:00 2001 From: danijoo Date: Sun, 27 Nov 2022 10:59:54 +0100 Subject: [PATCH 2/4] run app with gunicorn --- Dockerfile | 14 ++++++++++---- gunicorn.conf.py | 8 ++++++++ requirements.txt | 3 ++- 3 files changed, 20 insertions(+), 5 deletions(-) create mode 100644 gunicorn.conf.py diff --git a/Dockerfile b/Dockerfile index 8594b1c..e332aac 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,14 +1,17 @@ -# build pdfact +# STEP 1: build pdfact FROM ubuntu:20.04 + ENV DEBIAN_FRONTEND=noninteractive RUN apt-get update -y && apt-get upgrade -y && apt-get install -y maven git WORKDIR / +# TODO freeze revision? RUN git clone https://github.com/ad-freiburg/pdfact.git WORKDIR pdfact RUN mvn install -DskipTests +# STEP 2: run service FROM alpine:latest WORKDIR / @@ -26,12 +29,15 @@ RUN apk add --no-cache python3 \ && python3 -m ensurepip \ && pip3 install --upgrade pip setuptools +# set environment variables +ENV PYTHONDONTWRITEBYTECODE 1 +ENV PYTHONUNBUFFERED 1 # install flask app WORKDIR /app COPY ./requirements.txt . RUN pip install -r requirements.txt -COPY app.py . -ENTRYPOINT ["python3"] -CMD ["app.py"] +COPY app.py gunicorn.conf.py ./ + +ENTRYPOINT ["gunicorn", "--conf", "gunicorn.conf.py", "--bind", "0.0.0.0:80", "app:app"] diff --git a/gunicorn.conf.py b/gunicorn.conf.py new file mode 100644 index 0000000..795268c --- /dev/null +++ b/gunicorn.conf.py @@ -0,0 +1,8 @@ +loglevel = "info" +errorlog = "-" # stderr +accesslog = "-" # stdout +worker_tmp_dir = "/dev/shm" +graceful_timeout = 120 +timeout = 120 +keepalive = 5 +threads = 4 \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 3c78d8f..bd753fb 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1 +1,2 @@ -Flask==2.2.2 \ No newline at end of file +Flask==2.2.2 +gunicorn==20.1.0 \ No newline at end of file From 9ca1f9bcedc776c2c7c9c5d8b6bb4fcfb42ac4db Mon Sep 17 00:00:00 2001 From: danijoo Date: Sun, 27 Nov 2022 12:10:04 +0100 Subject: [PATCH 3/4] analyze file and return result --- Dockerfile | 4 +-- app.py | 54 +++++++++++++++++++++++++--- gunicorn.conf.py => gunicorn_conf.py | 0 3 files changed, 51 insertions(+), 7 deletions(-) rename gunicorn.conf.py => gunicorn_conf.py (100%) diff --git a/Dockerfile b/Dockerfile index e332aac..6d8bc68 100644 --- a/Dockerfile +++ b/Dockerfile @@ -38,6 +38,6 @@ WORKDIR /app COPY ./requirements.txt . RUN pip install -r requirements.txt -COPY app.py gunicorn.conf.py ./ +COPY app.py gunicorn_conf.py ./ -ENTRYPOINT ["gunicorn", "--conf", "gunicorn.conf.py", "--bind", "0.0.0.0:80", "app:app"] +ENTRYPOINT ["gunicorn", "--conf", "gunicorn_conf.py", "--bind", "0.0.0.0:80", "app:app"] diff --git a/app.py b/app.py index 79c4aa6..197f3fa 100644 --- a/app.py +++ b/app.py @@ -1,12 +1,56 @@ -from flask import Flask +from flask import Flask, request, flash, Response +from werkzeug.utils import secure_filename +import logging +from os import path, remove +import tempfile +import subprocess as sp app = Flask(__name__) -@app.route('/', methods=["GET"]) -def hello_world(): # put application's code here - return 'Hello World!' +def get_format(req, fallback=("application/json", "json")): + accept = req.headers.get("accept").lower() + if accept == "application/json": + return accept, "json" + elif accept == "application/xml": + return accept, "xml" + elif accept == "text/plain": + return accept, "txt" + else: + return fallback +@app.route('/analyze', methods=["POST"]) +def analyze(): + if "file" not in request.files or len(request.files["file"].filename) == 0: + flash("No file.") + return + + # save file to tmp folder + file = request.files["file"] + file_path = path.join(tempfile.gettempdir(), secure_filename(file.filename)) + app.logger.debug(f"saving file as {file_path}") + file.save(file_path) + + + + mime, response_format = get_format(request) + cmd = ["java", "-jar", "/pdfact.jar", file_path, "--format", response_format] + app.logger.debug(f"running {' '.join(cmd)}") + + try: + result = sp.check_output(cmd) + return Response(result, mimetype=mime) + except sp.CalledProcessError: + return "Something went wrong", 500 + finally: + remove(file_path) + if __name__ == '__main__': - app.run(host="0.0.0.0", port=5000) + app.run() +else: + # use gunicorns loglevel + gunicorn_logger = logging.getLogger("gunicorn.error") + app.logger.handlers = gunicorn_logger.handlers + app.logger.setLevel(gunicorn_logger.level) + app.logger.warn(gunicorn_logger.level) diff --git a/gunicorn.conf.py b/gunicorn_conf.py similarity index 100% rename from gunicorn.conf.py rename to gunicorn_conf.py From d6c4f6a0fc5032e725a07691432af6a22de01623 Mon Sep 17 00:00:00 2001 From: danijoo Date: Sun, 27 Nov 2022 12:16:45 +0100 Subject: [PATCH 4/4] ignore pycache --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 82195aa..685450e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ venv -.idea \ No newline at end of file +.idea +__pycache__ \ No newline at end of file