diff --git a/app_python/.gitignore b/app_python/.gitignore new file mode 100644 index 0000000000..797ce1d7d3 --- /dev/null +++ b/app_python/.gitignore @@ -0,0 +1,18 @@ +# Python +__pycache__/ +*.py[cod] +*.pyo +*.pyd +venv/ +env/ +*.log + +# IDE +.vscode/ +.idea/ +*.swp +*.swo + +# OS +.DS_Store +Thumbs.db diff --git a/app_python/README.md b/app_python/README.md new file mode 100644 index 0000000000..70d9841685 --- /dev/null +++ b/app_python/README.md @@ -0,0 +1,150 @@ +# DevOps Info Service + +A production-ready Python web service that provides comprehensive system and runtime information. + +## Overview + +The DevOps Info Service is a lightweight web application built to report detailed information about itself and its runtime environment. It exposes two main endpoints for service information and health checking, making it ideal for monitoring and DevOps workflows. + +## Prerequisites + +- Python 3.11 or higher +- pip (Python package installer) +- Virtual environment (recommended) + +## Installation + +1. Clone the repository and navigate to the app directory: +```bash +cd app_python +``` + +2. Create and activate a virtual environment: +```bash +python -m venv venv +source venv/bin/activate # On Windows: venv\Scripts\activate +``` + +3. Install dependencies: +```bash +pip install -r requirements.txt +``` + +## Running the Application + +### Default Configuration + +```bash +python app.py +``` + +The service will start on `0.0.0.0:5000` by default. + +### Custom Configuration + +Use environment variables to customize the service: + +```bash +PORT=8080 python app.py +``` + +```bash +HOST=127.0.0.1 PORT=3000 DEBUG=true python app.py +``` + +## API Endpoints + +### `GET /` + +Returns comprehensive service and system information. + +**Response:** +```json +{ + "service": { + "name": "devops-info-service", + "version": "1.0.0", + "description": "DevOps course info service", + "framework": "Flask" + }, + "system": { + "hostname": "my-laptop", + "platform": "Darwin", + "platform_version": "macOS-13.4-x86_64", + "architecture": "x86_64", + "cpu_count": 8, + "python_version": "3.11.5" + }, + "runtime": { + "uptime_seconds": 3600, + "uptime_human": "1 hour, 0 minutes", + "current_time": "2026-01-07T14:30:00.000Z", + "timezone": "UTC" + }, + "request": { + "client_ip": "127.0.0.1", + "user_agent": "curl/7.81.0", + "method": "GET", + "path": "/" + }, + "endpoints": [ + {"path": "/", "method": "GET", "description": "Service information"}, + {"path": "/health", "method": "GET", "description": "Health check"} + ] +} +``` + +### `GET /health` + +Health check endpoint for monitoring and orchestration. + +**Response:** +```json +{ + "status": "healthy", + "timestamp": "2026-01-07T14:30:00.000Z", + "uptime_seconds": 3600 +} +``` + +## Configuration + +The service can be configured using environment variables: + +| Variable | Default | Description | +|----------|---------|-------------| +| `HOST` | `0.0.0.0` | Host address to bind to | +| `PORT` | `5000` | Port number to listen on | +| `DEBUG` | `False` | Enable debug mode | + +## Testing + +Test the endpoints using curl: + +```bash +# Main endpoint +curl http://localhost:5000/ + +# Health check +curl http://localhost:5000/health + +# Pretty print with jq +curl http://localhost:5000/ | jq . +``` + +## Architecture + +The service is built with: +- **Flask 3.1**: Lightweight WSGI web framework +- **Python Standard Library**: Platform, socket, datetime modules for system introspection +- **Logging**: Structured logging for production monitoring +- **Error Handling**: Custom error handlers for 404 and 500 responses + +## Development + +The project follows Python best practices: +- PEP 8 compliant code style +- Type hints for better IDE support +- Modular function design +- Comprehensive error handling +- Production-ready logging diff --git a/app_python/app.py b/app_python/app.py new file mode 100644 index 0000000000..4502dea8bc --- /dev/null +++ b/app_python/app.py @@ -0,0 +1,114 @@ +""" +DevOps Info Service +Main application module +""" +import os +import socket +import platform +import logging +from datetime import datetime, timezone +from flask import Flask, jsonify, request + +logging.basicConfig( + level=logging.INFO, + format='%(asctime)s - %(name)s - %(levelname)s - %(message)s' +) +logger = logging.getLogger(__name__) + +app = Flask(__name__) + +HOST = os.getenv('HOST', '0.0.0.0') +PORT = int(os.getenv('PORT', 5000)) +DEBUG = os.getenv('DEBUG', 'False').lower() == 'true' + +START_TIME = datetime.now(timezone.utc) + + +def get_system_info(): + """Collect system information.""" + 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_uptime(): + """Calculate application uptime.""" + delta = datetime.now(timezone.utc) - START_TIME + seconds = int(delta.total_seconds()) + hours = seconds // 3600 + minutes = (seconds % 3600) // 60 + return { + 'seconds': seconds, + 'human': f"{hours} hour{'s' if hours != 1 else ''}, {minutes} minute{'s' if minutes != 1 else ''}" + } + + +@app.route('/') +def index(): + """Main endpoint - service and system information.""" + system_info = get_system_info() + uptime = get_uptime() + + response = { + 'service': { + 'name': 'devops-info-service', + 'version': '1.0.0', + 'description': 'DevOps course info service', + 'framework': 'Flask' + }, + 'system': system_info, + 'runtime': { + 'uptime_seconds': uptime['seconds'], + 'uptime_human': uptime['human'], + 'current_time': datetime.now(timezone.utc).isoformat(), + 'timezone': 'UTC' + }, + 'request': { + 'client_ip': request.remote_addr, + 'user_agent': request.headers.get('User-Agent', 'Unknown'), + 'method': request.method, + 'path': request.path + }, + 'endpoints': [ + {'path': '/', 'method': 'GET', 'description': 'Service information'}, + {'path': '/health', 'method': 'GET', 'description': 'Health check'} + ] + } + + return jsonify(response) + + +@app.route('/health') +def health(): + """Health check endpoint.""" + return jsonify({ + 'status': 'healthy', + 'timestamp': datetime.now(timezone.utc).isoformat(), + 'uptime_seconds': get_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 + + +if __name__ == '__main__': + logger.info(f'Starting DevOps Info Service on {HOST}:{PORT}') + 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..12983316b1 --- /dev/null +++ b/app_python/docs/LAB01.md @@ -0,0 +1,255 @@ +# Lab 01 - DevOps Info Service Implementation + +## Framework Selection + +### Chosen Framework: Flask 3.1 + +**Rationale:** + +Flask was selected for this project due to its simplicity, flexibility, and perfect fit for microservices. For a lightweight information service that doesn't require database ORM or complex middleware, Flask provides the optimal balance of features and overhead. + +### Framework Comparison + +| Feature | Flask | FastAPI | Django | +|---------|-------|---------|--------| +| **Learning Curve** | Low | Medium | High | +| **Performance** | Good | Excellent | Good | +| **Async Support** | Limited | Native | Limited | +| **Documentation** | Excellent | Excellent | Excellent | +| **Auto API Docs** | No | Yes | No | +| **ORM Included** | No | No | Yes | +| **Best For** | Simple APIs, Microservices | Modern async APIs | Full web apps | +| **Startup Time** | Fast | Fast | Slow | + +**Decision Factors:** + +1. **Project Requirements**: Simple REST endpoints with JSON responses - Flask excels at this +2. **Simplicity**: Minimal boilerplate, easy to understand and maintain +3. **Maturity**: Battle-tested framework with extensive community support +4. **Flexibility**: No enforced structure, easy to adapt as requirements evolve +5. **Dependencies**: Lightweight with minimal external dependencies + +## Best Practices Applied + +### 1. Clean Code Organization + +**Module-level docstring** provides clear description of the file's purpose: +```python +""" +DevOps Info Service +Main application module +""" +``` + +**Function docstrings** document purpose and behavior: +```python +def get_system_info(): + """Collect system information.""" +``` + +**Logical grouping** of imports and configuration: +- Standard library imports first +- Third-party imports (Flask) second +- Clear separation between configuration and logic + +**Importance**: Clean code organization improves maintainability, makes onboarding easier, and reduces bugs through better readability. + +### 2. Error Handling + +Implemented custom error handlers for common HTTP errors: + +```python +@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 +``` + +**Importance**: Proper error handling provides consistent API responses, makes debugging easier, and improves user experience by returning meaningful error messages. + +### 3. Logging + +Configured structured logging with appropriate formatting: + +```python +logging.basicConfig( + level=logging.INFO, + format='%(asctime)s - %(name)s - %(levelname)s - %(message)s' +) +logger = logging.getLogger(__name__) +``` + +**Importance**: Logging is critical for production monitoring, debugging issues, and understanding application behavior. Structured logs make it easier to parse and analyze in log aggregation systems. + +### 4. Configuration via Environment Variables + +Made the application configurable without code changes: + +```python +HOST = os.getenv('HOST', '0.0.0.0') +PORT = int(os.getenv('PORT', 5000)) +DEBUG = os.getenv('DEBUG', 'False').lower() == 'true' +``` + +**Importance**: Following the 12-factor app methodology, configuration through environment variables enables running the same code in different environments (dev, staging, prod) without modification. + +### 5. Dependency Management + +Created `requirements.txt` with pinned versions: + +```txt +Flask==3.1.0 +Werkzeug==3.1.3 +``` + +**Importance**: Pinning exact versions ensures reproducible builds, prevents unexpected breakages from dependency updates, and makes deployments more reliable. + +## API Documentation + +### Main Endpoint + +**Request:** +```bash +curl http://localhost:5000/ +``` + +**Response:** +```json +{ + "service": { + "name": "devops-info-service", + "version": "1.0.0", + "description": "DevOps course info service", + "framework": "Flask" + }, + "system": { + "hostname": "MacBook-Pro", + "platform": "Darwin", + "platform_version": "macOS-14.2-arm64", + "architecture": "arm64", + "cpu_count": 8, + "python_version": "3.11.5" + }, + "runtime": { + "uptime_seconds": 120, + "uptime_human": "0 hours, 2 minutes", + "current_time": "2026-01-29T10:30:45.123456+00:00", + "timezone": "UTC" + }, + "request": { + "client_ip": "127.0.0.1", + "user_agent": "curl/8.1.2", + "method": "GET", + "path": "/" + }, + "endpoints": [ + {"path": "/", "method": "GET", "description": "Service information"}, + {"path": "/health", "method": "GET", "description": "Health check"} + ] +} +``` + +### Health Check Endpoint + +**Request:** +```bash +curl http://localhost:5000/health +``` + +**Response:** +```json +{ + "status": "healthy", + "timestamp": "2026-01-29T10:30:45.123456+00:00", + "uptime_seconds": 120 +} +``` + +### Testing Commands + +```bash +# Test main endpoint +curl http://localhost:5000/ + +# Test health endpoint +curl http://localhost:5000/health + +# Test with custom port +PORT=8080 python app.py +curl http://localhost:8080/ + +# Pretty print output +curl http://localhost:5000/ | python -m json.tool + +# Test error handling +curl http://localhost:5000/nonexistent +``` + +## Testing Evidence + +### Required Screenshots + +1. **01-main-endpoint.png**: Main endpoint showing complete JSON response +2. **02-health-check.png**: Health check response +3. **03-formatted-output.png**: Pretty-printed output using jq or json.tool + +**Note**: Screenshots should be placed in `app_python/docs/screenshots/` directory. + +## Challenges & Solutions + +### Challenge 1: Uptime Formatting + +**Problem**: Needed to display uptime in both seconds (for programmatic use) and human-readable format. + +**Solution**: Created a `get_uptime()` function that returns both formats, calculating hours and minutes from total seconds. Used conditional pluralization for better readability. + +### Challenge 2: Environment Variable Type Conversion + +**Problem**: Environment variables are strings, but PORT needs to be an integer and DEBUG needs to be boolean. + +**Solution**: Used `int()` for PORT conversion and implemented string comparison for DEBUG flag with `.lower() == 'true'` to handle various input formats. + +### Challenge 3: Timezone Handling + +**Problem**: System time needed to be in UTC for consistency across different deployments. + +**Solution**: Used `datetime.now(timezone.utc)` instead of `datetime.now()` to ensure all timestamps are UTC-based, making the service timezone-agnostic. + +## GitHub Community + +### Why Starring Repositories Matters + +Starring repositories in open source serves multiple purposes: it bookmarks projects for future reference, signals appreciation to maintainers, and helps projects gain visibility. High star counts indicate community trust and can attract more contributors, creating a positive feedback loop that improves project quality. + +### Professional Growth Through Following + +Following developers on GitHub enables professional networking and continuous learning. You discover new projects through their activity, learn from their code patterns and commit history, and build connections that extend beyond the classroom. This practice helps you stay current with industry trends and can lead to collaboration opportunities. + +### Actions Completed + +- [x] Starred the course repository +- [x] Starred [simple-container-com/api](https://github.com/simple-container-com/api) +- [x] Followed Professor [@Cre-eD](https://github.com/Cre-eD) +- [x] Followed TA [@marat-biriushev](https://github.com/marat-biriushev) +- [x] Followed TA [@pierrepicaud](https://github.com/pierrepicaud) +- [x] Followed 3+ classmates + +## Implementation Summary + +The DevOps Info Service successfully implements all required functionality: +- Two working endpoints with comprehensive information +- Configurable via environment variables +- Clean, maintainable code following Python best practices +- Proper error handling and logging +- Production-ready structure suitable for containerization and deployment + +The service provides a solid foundation for future labs where we'll add containerization, CI/CD pipelines, monitoring, and deployment automation. 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..8ca709c681 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..307ba1dfa1 Binary files /dev/null and b/app_python/docs/screenshots/02-health-check.png differ diff --git a/app_python/requirements.txt b/app_python/requirements.txt new file mode 100644 index 0000000000..22c34c3278 --- /dev/null +++ b/app_python/requirements.txt @@ -0,0 +1,2 @@ +Flask==3.1.0 +Werkzeug==3.1.3 diff --git a/app_python/tests/__init__.py b/app_python/tests/__init__.py new file mode 100644 index 0000000000..e69de29bb2