diff --git a/app_python/.gitignore b/app_python/.gitignore new file mode 100644 index 0000000000..ba1db0ed69 --- /dev/null +++ b/app_python/.gitignore @@ -0,0 +1,12 @@ +# Python +__pycache__/ +*.py[cod] +venv/ +*.log + +# IDE +.vscode/ +.idea/ + +# OS +.DS_Store diff --git a/app_python/README.md b/app_python/README.md new file mode 100644 index 0000000000..1aaf6bdf64 --- /dev/null +++ b/app_python/README.md @@ -0,0 +1,69 @@ +# DevOps Info Service +## Overview + +This app is an educational project that reports system information. + +## Prerequisites + +To successfully install this app, you will need the following programs: +- Python 3.11+ +- pip + +During installation, the following modules will be acquired automatically as app dependencies: +- Flask 3.1.0 +- gunicorn + +## Installation (Linux) + +While in the same directory as this README file, execute the following: + +```bash +python -m venv venv +source venv/bin/activate +pip install -r requirements.txt +``` + +## Running the Application + +### With `gunicorn` + +It is best to run Flask application through an external manager program, in this case, `gunicorn`: + +```bash +gunicorn -b 127.0.0.1:5000 -e DEBUG=true app:app +``` + +The `DEBUG` flag allows the app to log web requests. Do not specify this flag to disable request logging: + +```bash +gunicorn -b 127.0.0.1:5000 app:app +``` + +Note that environment variables `HOST` and `PORT` are ignored if the app is run with `gunicorn`. + +### Directly + +With request logging: + +```bash +DEBUG=true python app.py +``` + +Without request logging: + +```bash +python app.py +``` + +## API Endpoints + +- `GET /` - Service and system information +- `GET /health` - Health check + +## Configuration + +| Name | Default value | Meaning | `gunicorn` behavior | +| --- | --- | --- | --- | +| `HOST` | `0.0.0.0` | On which interface to accept connections. Set to `127.0.0.1` to disallow outside connections. | Ignored | +| `PORT` | 5000 | To which port to bind. When connecting to the server, specify this port. | Ignored | +| `DEBUG` | `false` | If `true`, log web requests to console. | Used | diff --git a/app_python/app.py b/app_python/app.py new file mode 100644 index 0000000000..07b90cf6c8 --- /dev/null +++ b/app_python/app.py @@ -0,0 +1,116 @@ +""" +DevOps Info Service +Main application module +""" +from datetime import datetime, timezone +from flask import Flask, jsonify, request +import logging +import os +import platform +import socket +HOST = os.getenv('HOST', '0.0.0.0') +PORT = int(os.getenv('PORT', 5000)) +DEBUG = os.getenv('DEBUG', 'False').lower() == 'true' + +logging.basicConfig( + level=logging.INFO if not DEBUG else logging.DEBUG, + format='%(asctime)s - %(name)s - %(levelname)s - %(message)s' +) +logger = logging.getLogger(__name__) +app = Flask(__name__) + + +def get_system_info() -> dict[str, str | int | None]: + """Collect system information.""" + return { + 'hostname': socket.gethostname(), + 'platform': platform.system(), + 'platform_version': platform.version(), + 'architecture': platform.machine(), + 'cpu_count': os.cpu_count(), + 'python_version': platform.python_version() + } + + +def get_uptime() -> dict[str, int | str | None]: + def word_plural(number: int) -> str: + """Used for grammar when composing human-readable uptime (uptime_human)""" + return 's' if number != 1 else '' + + now = datetime.now(timezone.utc) + delta = now - START_TIME + seconds = int(delta.total_seconds()) + hours = seconds // 3600 + minutes = (seconds % 3600) // 60 + loctime = now.astimezone() + tzinfo = loctime.tzinfo + if tzinfo is not None: + tzinfo = str(tzinfo) + return { + 'uptime_seconds': seconds, + 'uptime_human': f"{hours} hour{word_plural(hours)}, {minutes} minute{word_plural(minutes)}", + 'current_time': loctime.isoformat(timespec='milliseconds'), + 'timezone': tzinfo, + } + + +def get_request_info() -> dict[str, str | None]: + return { + "client_ip": request.remote_addr, + "user_agent": request.headers.get('User-Agent'), + "method": request.method, + "path": request.path, + } + + +@app.route('/') +def index(): + logger.debug(f'Request: {request.method} {request.path}') + """Main endpoint - service and system information.""" + return jsonify({ + 'service': { + 'name': 'devops-info-service', + 'version': '1.0.0', + 'description': 'DevOps course info service', + 'framework': 'Flask' + }, + 'system': get_system_info(), + 'runtime': get_uptime(), + 'request': get_request_info(), + 'endpoints': [ + {"path": "/" , "method": "GET", "description": "Service information"}, + {"path": "/health", "method": "GET", "description": "Health check"} + ] + }) + + +@app.route('/health') +def health(): + logger.debug(f'Request: {request.method} {request.path}') + return jsonify({ + 'status': 'healthy', + 'timestamp': datetime.now(timezone.utc).isoformat(), + 'uptime_seconds': get_uptime()['uptime_seconds'] + }) + + +@app.errorhandler(404) +def not_found(error): + return jsonify({ + 'error': 'Not Found', + 'message': 'Endpoint does not exist' + }), 404 + + +@app.errorhandler(500) +def internal_error(error): + return jsonify({ + 'error': 'Internal Server Error', + 'message': 'An unexpected error occurred' + }), 500 + + +START_TIME = datetime.now(timezone.utc) +logger.info('Application starting...') +if __name__ == '__main__': + app.run(host=HOST, port=PORT, debug=DEBUG) diff --git a/app_python/docs/LAB01.md b/app_python/docs/LAB01.md new file mode 100644 index 0000000000..260c55a7a6 --- /dev/null +++ b/app_python/docs/LAB01.md @@ -0,0 +1,169 @@ +## Framework selection + +For this project, I chose the Flask web framework because: +1. I am most familiar with it; +2. its simplicity will help me to complete labs quickly in the future :) + +| Framework | Asynchronous | Complex Features | Learning Curve | Verdict | +| --- | --- | --- | --- | --- | +| Flask | NO | ORM, authentication, caching | Easy to learn | Fit for our purposes | +| FastAPI | YES | Strongly typed models, automatic API documentation, WebSocket support | Steep | Too much setup for unused features | +| Django | YES | Robust ORM, extensive libraries (e.g., authentication, admin), scalable architecture | Steep | Overkill for small projects | + +## Best Practices Applied +- Typed functions: + ```python + def get_system_info() -> dict[str, str | int | None]: + def get_uptime() -> dict[str, int | str | None]: + def word_plural(number: int) -> str: + def get_request_info() -> dict[str, str | None]: + ``` + This helps not forget what the functions expect as arguments and what output they return. Also, some LSPs (like my + favorite, `pyright`) offer type checking when this is done. +- Docstrings: + ```python + def word_plural(number: int) -> str: + """Used for grammar when composing human-readable uptime (uptime_human)""" + ``` + Increases maintainability. Also, my IDE shows docstrings in tooltips, which is helpful. + +## API Documentation +### Request/response examples +See next subsection (**Testing commands**). + +### Testing commands + +Index: +```sh +curl http://localhost:5000/ 2>/dev/null | jq +``` +Response: +```json +{ + "endpoints": [ + { + "description": "Service information", + "method": "GET", + "path": "/" + }, + { + "description": "Health check", + "method": "GET", + "path": "/health" + } + ], + "request": { + "client_ip": "127.0.0.1", + "method": "GET", + "path": "/", + "user_agent": "curl/8.18.0" + }, + "runtime": { + "current_time": "2026-01-26T15:56:22.043+03:00", + "timezone": "MSK", + "uptime_human": "0 hours, 2 minutes", + "uptime_seconds": 160 + }, + "service": { + "description": "DevOps course info service", + "framework": "Flask", + "name": "devops-info-service", + "version": "1.0.0" + }, + "system": { + "architecture": "x86_64", + "cpu_count": 16, + "hostname": "timur-ficus", + "platform": "Linux", + "platform_version": "#1 SMP PREEMPT_DYNAMIC Sun, 18 Jan 2026 00:34:07 +0000", + "python_version": "3.14.2" + } +} +``` + +Health: +```sh +curl http:///localhost:5000/health 2>/dev/null | jq +``` +Response: +```json +{ + "status": "healthy", + "timestamp": "2026-01-26T12:57:30.745116+00:00", + "uptime_seconds": 229 +} +``` + +## Testing Evidence + +### Screenshots showing endpoints work + +Main endpoint: + +![Main endpoint](/app_python/docs/screenshots/01-main-endpoint.png) + +Health check: + +![Health check](/app_python/docs/screenshots/02-health-check.png) + +Formatted output: + +![Formatted output](/app_python/docs/screenshots/03-formatted-output.png) + +### Terminal output +See section **API Documentation**, subsection **Testing commands**. + +## Challenges & Solutions + +When programming the endpoints, I encountered the problem of formatting `datetime` objects and converting between +timezones. But it was easy to find a solution on Stack Overflow: +[Stack Overflow](https://stackoverflow.com/questions/2720319/python-figure-out-local-timezone). + +I am familiar with writing small apps on Flask, so this framework did not present any challenges. + +## GitHub Community + +### Why Stars Matter + +**Discovery & Bookmarking:** +- Stars help you bookmark interesting projects for later reference +- Star count indicates project popularity and community trust +- Starred repos appear in your GitHub profile, showing your interests + +**Open Source Signal:** +- Stars encourage maintainers (shows appreciation) +- High star count attracts more contributors +- Helps projects gain visibility in GitHub search and recommendations + +**Professional Context:** +- Shows you follow best practices and quality projects +- Indicates awareness of industry tools and trends + +### Why Following Matters + +**Networking:** +- See what other developers are working on +- Discover new projects through their activity +- Build professional connections beyond the classroom + +**Learning:** +- Learn from others' code and commits +- See how experienced developers solve problems +- Get inspiration for your own projects + +**Collaboration:** +- Stay updated on classmates' work +- Easier to find team members for future projects +- Build a supportive learning community + +**Career Growth:** +- Follow thought leaders in your technology stack +- See trending projects in real-time +- Build visibility in the developer community + +**GitHub Best Practices:** +- Star repos you find useful (not spam) +- Follow developers whose work interests you +- Engage meaningfully with the community +- Your GitHub activity shows employers your interests and involvement + diff --git a/app_python/docs/screenshots/01-main-endpoint.png b/app_python/docs/screenshots/01-main-endpoint.png new file mode 100644 index 0000000000..a379c67558 Binary files /dev/null and b/app_python/docs/screenshots/01-main-endpoint.png differ diff --git a/app_python/docs/screenshots/02-health-check.png b/app_python/docs/screenshots/02-health-check.png new file mode 100644 index 0000000000..52f6d05a23 Binary files /dev/null and b/app_python/docs/screenshots/02-health-check.png differ diff --git a/app_python/docs/screenshots/03-formatted-output.png b/app_python/docs/screenshots/03-formatted-output.png new file mode 100644 index 0000000000..38919fd7b4 Binary files /dev/null and b/app_python/docs/screenshots/03-formatted-output.png differ diff --git a/app_python/requirements.txt b/app_python/requirements.txt new file mode 100644 index 0000000000..f0950a10ff --- /dev/null +++ b/app_python/requirements.txt @@ -0,0 +1,2 @@ +Flask==3.1.0 +gunicorn==24.0.0 diff --git a/app_python/tests/__init__.py b/app_python/tests/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/compiled_app/README.md b/compiled_app/README.md new file mode 100644 index 0000000000..ef6be2599e --- /dev/null +++ b/compiled_app/README.md @@ -0,0 +1,24 @@ +# Where is it? + +Dear course team, + +Please check out a small **compiled** website I made back in November of 2025: + +[d-language.website](https://d-language.website) + +And, specifically, the Dockerfile for the server: + +[Dockerfile](https://github.com/Error10556/dsite/blob/main/site/Dockerfile) + +This is a showcase for an interpreted language we made for the Compiler Construction course. However, +this interpreted language is called from C++ through an interpreter library that gets compiled from source in a +2-stage build! Dmitry Creed said during the lecture that this is the main point of the bonus task. + +## How it works + +Through NginX, the client connects to the server written in C++ using Oat++ framework. The server handles connections. +When a page is requested, the C++ server uses the D language library (compiled!) to call an interpreted function that +would return the HTML page text or an image. + +Sure, the site was originally meant to showcase the interpreter, but I feel like the compiled part also deserves +appreciation.