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/.dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
__pycache__/
*.pyc
*.pyo
*.pyd
.venv/
venv/
env/
.git
.gitignore
.vscode/
.idea/
tests/
docs/
README.md
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
20 changes: 20 additions & 0 deletions app_python/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
FROM python:3.13-slim

ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1 \
PIP_NO_CACHE_DIR=1

WORKDIR /app

RUN addgroup --system appgroup && adduser --system --ingroup appgroup appuser

COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY --chown=appuser:appgroup app.py .

USER appuser

EXPOSE 5000

CMD ["python", "app.py"]
134 changes: 134 additions & 0 deletions app_python/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
# 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. Tiny app, serious vibes, useful JSON (ง'̀-'́)ง

It exposes two endpoints:
- `GET /` — detailed service and system info
- `GET /health` — lightweight health check for monitoring and quick status checks (•‿•)

## Prerequisites
Before waking up the app, make sure you have:

- Python 3.11+
- `pip`
- optional virtual environment, because clean environments are happy environments (づ。◕‿‿◕。)づ

## Installation

### Create and activate virtual environment

**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

From the `app_python/` directory:

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

## Running the Application

### Default run
From the `app_python/` directory:

```bash
python app.py
```

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

### Run with custom configuration
You can change the port through an environment variable, very DevOps, very fancy (。•̀ᴗ-)✧

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

## API Endpoints

### `GET /`
Returns detailed information about:
- the service itself
- the system it runs on
- runtime information
- request metadata

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

### `GET /health`
Returns a simple health response suitable for monitoring and automated checks.

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

## Configuration

| Variable | Default | Example | Description |
|----------|-----------|-------------|-------------------------|
| `HOST` | `0.0.0.0` | `127.0.0.1` | Bind address |
| `PORT` | `5000` | `8080` | Port number |
| `DEBUG` | `False` | `True` | Enable Flask debug mode |

## Framework Selection

**Chosen framework:** Flask

**Why Flask:** Flask is lightweight, beginner-friendly, and requires very little boilerplate. For a small JSON API with only two endpoints, it is a perfect fit and does not add unnecessary complexity (・ω・)b

## Best Practices Applied

### Clean code organization
- imports are grouped properly
- helper functions are extracted into separate reusable units
- endpoint logic is kept readable and compact

### Error handling
- `404` returns JSON instead of HTML
- `500` returns JSON instead of HTML

### Logging
- startup logging is enabled
- request logging is enabled
- this helps with debugging, monitoring, and knowing what the app is doing instead of guessing into the void (⊙_⊙)

## Docker

### Build image locally
```bash
docker build -t <image-name> .
```

### Run container
```bash
docker run --name <container-name> -p <host-port>:<container-port> <image-name>
```

### Pull from Docker Hub
```bash
docker pull <dockerhub-username>/<repository-name>:<tag>
```

### Run pulled image
```bash
docker run --rm -p <host-port>:<container-port> <dockerhub-username>/<repository-name>:<tag>
```
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}
```
Loading