Skip to content

Commit

Permalink
Lab 12, task 1
Browse files Browse the repository at this point in the history
  • Loading branch information
kolayne committed Apr 22, 2024
1 parent 7f85cb9 commit 5078463
Show file tree
Hide file tree
Showing 5 changed files with 60 additions and 2 deletions.
2 changes: 2 additions & 0 deletions app_python/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ COPY requirements.txt /app/
COPY moscow_time/ /app/moscow_time
# Note: keeping the project files owned by root so
# the web server has less privileges over them
RUN ["mkdir", "--mode", "777", "/app/persistent"]
VOLUME /app/persistent
USER flask:flask
RUN ["pip", "install", "--user", "-r", "requirements.txt"]
CMD ["python", "-m", "moscow_time"]
3 changes: 3 additions & 0 deletions app_python/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ docker run --rm -d -p 5000 kolay0ne/app_py

Replace `kolay0ne/app_py` with your image/tag name if you built it manually.

One may want to mount a volume or a bind-mount at `/app/persistent`, which acts
as a persistent storage for the visits counter of the web app.

## Unit Tests

To run unit tests:
Expand Down
24 changes: 23 additions & 1 deletion app_python/moscow_time/__init__.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import datetime
from os import makedirs
from time import monotonic

from flask import Flask, request, Response
from flask import Flask, request, Response, send_from_directory
import requests
import prometheus_client

from .cache import cache_for
from .visits import increment_on_call


app = Flask(__name__)
Expand Down Expand Up @@ -42,12 +44,32 @@ def get_time():
return dt.time()


VISITS_FILENAME = 'persistent/visits.bin'

# Create it on start
try:
basename_at = VISITS_FILENAME.rindex('/')
except ValueError:
pass
else:
makedirs(VISITS_FILENAME[:basename_at], exist_ok=True)
open(VISITS_FILENAME, 'a+').close() # The `a+` mode ensures we have write perm


@app.route('/')
@increment_on_call(VISITS_FILENAME)
def index():
time = get_time()
return f"In MSK it's {time.hour}:{time.minute}:{time.second}. " \
"Have you brushed your teeth today yet?"

@app.route('/metrics')
@increment_on_call(VISITS_FILENAME)
def prometheus_metrics():
return Response(prometheus_client.generate_latest(), mimetype='text/plain')

@app.route('/visits')
@increment_on_call(VISITS_FILENAME)
def visits():
with open(VISITS_FILENAME, 'rb') as f:
return str(int.from_bytes(f.read(), byteorder='little'))
28 changes: 28 additions & 0 deletions app_python/moscow_time/visits.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import fcntl
from functools import wraps
from threading import Lock
from typing import Callable


def increment(filename: str) -> None:
with open(filename, 'rb+') as f:
try:
fcntl.flock(f.fileno(), fcntl.LOCK_EX)
cur = int.from_bytes(f.read(), byteorder='little')
f.seek(0)
cur += 1
f.write(cur.to_bytes(byteorder='little', length=(cur.bit_length() // 8 + 1)))
f.truncate() # Not necessary, as larger numbers take more bytes
finally:
# Note: will be unlocked anyway when the file is closed
fcntl.flock(f.fileno(), fcntl.LOCK_UN)


def increment_on_call(filename: str) -> Callable[[Callable, ...], Callable]:
def decorator(func: Callable) -> Callable:
@wraps(func)
def with_increment(*args, **kwargs):
increment(filename)
return func(*args, **kwargs)
return with_increment
return decorator
5 changes: 4 additions & 1 deletion monitoring/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@ networks:

volumes:
grafana-storage:
py-persistent:

services:
app_py:
image: kolay0ne/app_py:lab8
image: kolay0ne/app_py:lab12
ports:
- "5000:5000"
logging:
Expand All @@ -17,6 +18,8 @@ services:
resources: {limits: {memory: 30M}}
networks:
- prometheus
volumes:
- py-persistent:/app/persistent

app_go:
image: kolay0ne/app_go:lab8
Expand Down

0 comments on commit 5078463

Please sign in to comment.