Skip to content

Commit 2b6d2b0

Browse files
committed
Lab 12, task 1
1 parent 7f85cb9 commit 2b6d2b0

File tree

6 files changed

+63
-2
lines changed

6 files changed

+63
-2
lines changed

app_python/.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,3 +174,6 @@ poetry.toml
174174
pyrightconfig.json
175175

176176
# End of https://www.toptal.com/developers/gitignore/api/python
177+
178+
179+
/persistent

app_python/Dockerfile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ COPY requirements.txt /app/
77
COPY moscow_time/ /app/moscow_time
88
# Note: keeping the project files owned by root so
99
# the web server has less privileges over them
10+
RUN ["mkdir", "--mode", "777", "/app/persistent"]
11+
VOLUME /app/persistent
1012
USER flask:flask
1113
RUN ["pip", "install", "--user", "-r", "requirements.txt"]
1214
CMD ["python", "-m", "moscow_time"]

app_python/README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,9 @@ docker run --rm -d -p 5000 kolay0ne/app_py
5454

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

57+
One may want to mount a volume or a bind-mount at `/app/persistent`, which acts
58+
as a persistent storage for the visits counter of the web app.
59+
5760
## Unit Tests
5861

5962
To run unit tests:

app_python/moscow_time/__init__.py

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
import datetime
2+
from os import makedirs
23
from time import monotonic
34

4-
from flask import Flask, request, Response
5+
from flask import Flask, request, Response, send_from_directory
56
import requests
67
import prometheus_client
78

89
from .cache import cache_for
10+
from .visits import increment_on_call
911

1012

1113
app = Flask(__name__)
@@ -42,12 +44,32 @@ def get_time():
4244
return dt.time()
4345

4446

47+
VISITS_FILENAME = 'persistent/visits.bin'
48+
49+
# Create it on start
50+
try:
51+
basename_at = VISITS_FILENAME.rindex('/')
52+
except ValueError:
53+
pass
54+
else:
55+
makedirs(VISITS_FILENAME[:basename_at], exist_ok=True)
56+
open(VISITS_FILENAME, 'a+').close() # The `a+` mode ensures we have write perm
57+
58+
4559
@app.route('/')
60+
@increment_on_call(VISITS_FILENAME)
4661
def index():
4762
time = get_time()
4863
return f"In MSK it's {time.hour}:{time.minute}:{time.second}. " \
4964
"Have you brushed your teeth today yet?"
5065

5166
@app.route('/metrics')
67+
@increment_on_call(VISITS_FILENAME)
5268
def prometheus_metrics():
5369
return Response(prometheus_client.generate_latest(), mimetype='text/plain')
70+
71+
@app.route('/visits')
72+
@increment_on_call(VISITS_FILENAME)
73+
def visits():
74+
with open(VISITS_FILENAME, 'rb') as f:
75+
return str(int.from_bytes(f.read(), byteorder='little'))

app_python/moscow_time/visits.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import fcntl
2+
from functools import wraps
3+
from threading import Lock
4+
from typing import Callable
5+
6+
7+
def increment(filename: str) -> None:
8+
with open(filename, 'rb+') as f:
9+
try:
10+
fcntl.flock(f.fileno(), fcntl.LOCK_EX)
11+
cur = int.from_bytes(f.read(), byteorder='little')
12+
f.seek(0)
13+
cur += 1
14+
f.write(cur.to_bytes(byteorder='little', length=(cur.bit_length() // 8 + 1)))
15+
f.truncate() # Not necessary, as larger numbers take more bytes
16+
finally:
17+
# Note: will be unlocked anyway when the file is closed
18+
fcntl.flock(f.fileno(), fcntl.LOCK_UN)
19+
20+
21+
def increment_on_call(filename: str) -> Callable[[Callable, ...], Callable]:
22+
def decorator(func: Callable) -> Callable:
23+
@wraps(func)
24+
def with_increment(*args, **kwargs):
25+
increment(filename)
26+
return func(*args, **kwargs)
27+
return with_increment
28+
return decorator

monitoring/docker-compose.yml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,11 @@ networks:
44

55
volumes:
66
grafana-storage:
7+
py-persistent:
78

89
services:
910
app_py:
10-
image: kolay0ne/app_py:lab8
11+
image: kolay0ne/app_py:lab12
1112
ports:
1213
- "5000:5000"
1314
logging:
@@ -17,6 +18,8 @@ services:
1718
resources: {limits: {memory: 30M}}
1819
networks:
1920
- prometheus
21+
volumes:
22+
- py-persistent:/app/persistent
2023

2124
app_go:
2225
image: kolay0ne/app_go:lab8

0 commit comments

Comments
 (0)