Skip to content
Open
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
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
14 changes: 14 additions & 0 deletions app_python/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Python
__pycache__/
*.py[cod]
*.log

# Virtual env
venv/

# IDE
.vscode/
.idea/

# OS
.DS_Store
118 changes: 118 additions & 0 deletions app_python/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
````md
# DevOps Info Service (Python) (。•̀ᴗ-)✧

## Overview
DevOps Info Service is a small Python web application that reports information about itself and the system it runs on (hostname, OS/platform, CPU count, Python version, uptime, and request metadata) (•‿•).
It exposes two endpoints: `GET /` for detailed service/system info and `GET /health` for a lightweight health check used by monitoring tools (ง'̀-'́)ง.

## Prerequisites
- Python 3.11+ (Python 3.12 works great too) (^▽^)/
- `pip` (usually comes with Python)
- (Recommended) Virtual environment (`venv`) so dependencies stay tidy (。•̀ᴗ-)✧

## Installation

### Create and activate virtual environment (venv) (づ。◕‿‿◕。)づ
**Windows (Git Bash):**
```bash
python -m venv venv
source venv/Scripts/activate
````

**Linux/Mac:**

```bash
python -m venv venv
source venv/bin/activate
```

### Install dependencies (let’s feed the app its snacks )

From the `app_python/` directory:

```bash
python -m pip install -r requirements.txt
```

## Running the Application

### Default run (simple & comfy) (・ω・)b

From the `app_python/` directory:

```bash
python app.py
```

Default address: `http://127.0.0.1:5000`

### Run with custom configuration (DevOps-style)

```bash
PORT=8080 python app.py
```

## API Endpoints

* `GET /` — Service and system information
Example:

```bash
curl http://127.0.0.1:5000/
```

* `GET /health` — Health check
Example:

```bash
curl http://127.0.0.1:5000/health
```

## Configuration (Environment Variables) (≧▽≦)

| Variable | Default | Example | Description |
| -------- | --------: | ----------: | --------------------------- |
| `HOST` | `0.0.0.0` | `127.0.0.1` | Bind address to listen on |
| `PORT` | `5000` | `8080` | Port number for the service |
| `DEBUG` | `False` | `True` | Enable Flask debug mode |

```
::contentReference[oaicite:0]{index=0}

## Framework selection

**Chosen framework:** Flask.

**Why Flask:** Flask is lightweight and beginner-friendly. It requires minimal boilerplate and is enough for a small JSON API with a couple of endpoints.

### Comparison table

| Framework | Pros | Cons | Why not chosen |
|---|---|---|---|
| Flask | Simple, minimal code, easy to learn | Fewer built-in features | N/A (chosen) |
| FastAPI | Modern, async, auto docs | More concepts (async, pydantic) | Not needed for 2 endpoints |
| Django | Full-featured, ORM, admin | Heavy for this lab | Overkill for simple service |

## Best practices apply

### Clean code organization
- Imports grouped (standard library first, then third-party).
- Helper functions extracted: `get_system_info()`, `get_uptime()`, `get_request_info()`.

### Error handling
- Implemented JSON error handlers:
- 404 Not Found returns JSON message.
- 500 Internal Server Error returns JSON message.

### Logging
- Configured logging using Python `logging`.
- Logs application startup and each request (method/path/client IP).

## API documentation

### GET /
Test:
```bash
curl http://127.0.0.1:5000/

```
134 changes: 134 additions & 0 deletions app_python/app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
"""
DevOps Info Service (Python) ── (。•̀ᴗ-)✧
Two endpoints:
- GET / -> full service + system + runtime + request info
- GET /health -> simple health check (monitoring-friendly) (•‿•)
"""

from __future__ import annotations

import logging
import os
import platform
import socket
from datetime import datetime, timezone
from typing import Any, Dict, Tuple

from flask import Flask, jsonify, request

# --- Logging: your app's tiny diary (ง'̀-'́)ง ---
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
)
logger = logging.getLogger("devops-info-service")

# --- App instance (hello, Flask!) (^▽^)/ ---
app = Flask(__name__)

# --- Config via env vars (DevOps superpower) (。•̀ᴗ-)✧ ---
HOST: str = os.getenv("HOST", "0.0.0.0")
PORT: int = int(os.getenv("PORT", "5000"))
DEBUG: bool = os.getenv("DEBUG", "False").lower() == "true"

# --- Start time for uptime (⏱️) ---
START_TIME = datetime.now(timezone.utc)


def iso_utc_now() -> str:
"""Current UTC time in ISO format (milliseconds) + Z (UTC) (•‿•)"""
return datetime.now(timezone.utc).isoformat(timespec="milliseconds").replace("+00:00", "Z")


def get_uptime() -> Tuple[int, str]:
"""Return uptime in seconds + human string (machine + human friendly) (・ω・)b"""
delta = datetime.now(timezone.utc) - START_TIME
seconds = int(delta.total_seconds())
hours = seconds // 3600
minutes = (seconds % 3600) // 60
return seconds, f"{hours} hour(s), {minutes} minute(s)"


def get_system_info() -> Dict[str, Any]:
"""Collect system info from the machine running this service (^▽^)"""
return {
"hostname": socket.gethostname(),
"platform": platform.system(),
"platform_version": platform.platform(),
"architecture": platform.machine(),
"cpu_count": os.cpu_count(),
"python_version": platform.python_version(),
}


def get_request_info() -> Dict[str, Any]:
"""Collect request info (who asked & how) (。•̀ᴗ-)✧"""
return {
"client_ip": request.remote_addr,
"user_agent": request.headers.get("User-Agent"),
"method": request.method,
"path": request.path,
}


def list_endpoints() -> list[dict]:
"""Self-documentation so the API can introduce itself (•‿•)"""
return [
{"path": "/", "method": "GET", "description": "Service information"},
{"path": "/health", "method": "GET", "description": "Health check"},
]


@app.get("/")
def main():
logger.info("Request: %s %s from %s", request.method, request.path, request.remote_addr)

uptime_seconds, uptime_human = get_uptime()
data = {
"service": {
"name": "devops-info-service",
"version": "1.0.0",
"description": "DevOps course info service",
"framework": "Flask",
},
"system": get_system_info(),
"runtime": {
"uptime_seconds": uptime_seconds,
"uptime_human": uptime_human,
"current_time": iso_utc_now(),
"timezone": "UTC",
},
"request": get_request_info(),
"endpoints": list_endpoints(),
}
return jsonify(data), 200


@app.get("/health")
def health():
logger.info("Request: %s %s from %s", request.method, request.path, request.remote_addr)

uptime_seconds, _ = get_uptime()
data = {
"status": "healthy",
"timestamp": iso_utc_now(),
"uptime_seconds": uptime_seconds,
}
return jsonify(data), 200


# --- Error handling: return JSON, not HTML pages (。•́︿•̀。) ---
@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):
logger.exception("Internal error: %s", error)
return jsonify({"error": "Internal Server Error", "message": "An unexpected error occurred"}), 500


if __name__ == "__main__":
logger.info("Starting application on %s:%s (DEBUG=%s) (づ。◕‿‿◕。)づ", HOST, PORT, DEBUG)
app.run(host=HOST, port=PORT, debug=DEBUG)
64 changes: 64 additions & 0 deletions app_python/docs/LAB01.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
````md
# LAB01 — Python Web Application

## 1) Framework choice
I chose **Flask** because it is pretty cool and since I am a beginner it was easy to learnd. It requires minimal boilerplate code, which is convenient for a small service with a few endpoints. Flask is enough for returning JSON responses and reading request information so why not.

## 2) How to run the service

### Create and activate venv (Windows Git Bash)
```bash
python -m venv venv
source venv/Scripts/activate
````

### Install dependencies

```bash
cd app_python
python -m pip install -r requirements.txt
```

### Run (default)

```bash
python app.py
```

### Run with custom host/port (optional)

```bash
HOST=127.0.0.1 PORT=8080 python app.py
```

## 3) How to test endpoints

### Health check

```bash
curl http://127.0.0.1:5000/health
```

### Main endpoint

```bash
curl http://127.0.0.1:5000/
```

### Formatted JSON output

```bash
curl -s http://127.0.0.1:5000/ | python -m json.tool
```

## 4) Screenshots (proof of work)

Screenshots are saved in `app_python/docs/screenshots/`:

* `01-main-endpoint.png`
* `02-health-check.png`
* `03-formatted-output.png`

```
::contentReference[oaicite:0]{index=0}
```
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.
Binary file added app_python/docs/screenshots/json_formatted.png
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.0.0
Flask==3.1.0
Empty file added app_python/tests/__init__.py
Empty file.
Loading