Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions app_python/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Python
__pycache__/
*.py[cod]
venv/
*.log

# IDE
.vscode/
.idea/

# OS
.DS_Store
69 changes: 69 additions & 0 deletions app_python/README.md
Original file line number Diff line number Diff line change
@@ -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 |
116 changes: 116 additions & 0 deletions app_python/app.py
Original file line number Diff line number Diff line change
@@ -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)
169 changes: 169 additions & 0 deletions app_python/docs/LAB01.md
Original file line number Diff line number Diff line change
@@ -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

Binary file added app_python/docs/screenshots/01-main-endpoint.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added app_python/docs/screenshots/02-health-check.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions app_python/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Flask==3.1.0
gunicorn==24.0.0
Empty file added app_python/tests/__init__.py
Empty file.
24 changes: 24 additions & 0 deletions compiled_app/README.md
Original file line number Diff line number Diff line change
@@ -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.