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
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions app_python/.dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# 🐙 Version control
.git
.gitignore

# 🐍 Python
__pycache__
*.pyc
*.pyo
venv/
.venv/

# 🔐 Secrets (NEVER include!)
.env
*.pem
secrets/

# 📝 Documentation
*.md
docs/

# 🧪 Tests (if not needed in container)
tests/
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
16 changes: 16 additions & 0 deletions app_python/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
FROM python:3.13-slim

WORKDIR /app

RUN useradd --create-home --shell /bin/bash appuser

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

COPY . .

USER appuser

EXPOSE 5000

CMD ["python", "app.py"]
103 changes: 103 additions & 0 deletions app_python/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
# DevOps Info Service

## Overview

Web service that reports system information and health status. Provides API endpoints for service info, hostname, platform, uptime, and request details.

## Prerequisites

- Python 3.11+
- pip

## Installation

```bash
python -m venv venv
venv\Scripts\activate
pip install -r requirements.txt
```

## Running

```bash
python app.py
```

Default: `http://0.0.0.0:5000`

**Custom config:**

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

## API Endpoints

### `GET /`

Returns service and system information.

```bash
curl http://localhost:5000/
```

### `GET /health`

Health check endpoint.

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

## Configuration

| Variable | Default | Description |
| -------- | --------- | ------------ |
| `HOST` | `0.0.0.0` | Host address |
| `PORT` | `5000` | Port number |
| `DEBUG` | `False` | Debug mode |

## Docker

### Build the image

```bash
docker build -t roma3213/info_service:1.0 .
```

### Run a container

```bash
docker run -p 5000:5000 roma3213/info_service:1.0
```

With custom port:

```bash
docker run -p 5000:5000 roma3213/info_service:1.0
```

### Pull from Docker Hub

```bash
docker pull roma3213/info_service:1.0
docker run -p 5000:5000 roma3213/info_service:1.0
```

## Project Structure

```
app_python/
├── app.py # Main app
├── config.py # Config
├── routes/ # API routes
├── services/ # Business logic
├── tests/
├── docs/ # Lab docs, screenshots
├── requirements.txt
├── Dockerfile # Container image
├── .dockerignore
├── .gitignore
└── README.md
```
42 changes: 42 additions & 0 deletions app_python/app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import os
import logging
from fastapi import FastAPI, Request, HTTPException
from fastapi.responses import JSONResponse
from routes.system_info import router as system_info_router
from config import HOST, PORT, DEBUG
import uvicorn

logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)

app = FastAPI()

app.include_router(system_info_router)

@app.exception_handler(404)
async def not_found(request: Request, exc: HTTPException):
return JSONResponse(
status_code=404,
content={
"error": "Not Found",
"message": "Endpoint does not exist"
}
)

@app.exception_handler(Exception)
async def internal_error(request: Request, exc: Exception):
return JSONResponse(
status_code=500,
content={
"error": "Internal Server Error",
"message": "An unexpected error occurred"
}
)

if __name__ == "__main__":
logger.info(f"Application started on {HOST}:{PORT}")
uvicorn.run(app, host=HOST, port=PORT)

5 changes: 5 additions & 0 deletions app_python/config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import os

HOST = os.getenv('HOST', '0.0.0.0')
PORT = int(os.getenv('PORT', 5000))
DEBUG = os.getenv('DEBUG', 'False').lower() == 'true'
67 changes: 67 additions & 0 deletions app_python/docs/LAB01.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# Lab 1 Submission

## Framework Selection

**FastAPI** - Modern, fast, has auto-docs and async support.

| Criteria | FastAPI | Flask | Django |
| --------- | --------- | ---------- | ------ |
| Speed | Very Fast | Fast | Medium |
| Auto Docs | Yes | No | No |
| Async | Yes | Limited | Yes |
| Size | Small | Very Small | Large |

**Why not Flask?** Flask is simpler but FastAPI has better async and auto-docs.

**Why not Django?** Too big for this simple service.

## Best Practices Applied

1. **Code Organization** - Separated into routes/, services/, config.py
2. **Error Handling** - 404 and 500 handlers
3. **Logging** - Basic logging setup
4. **Environment Variables** - Config via env vars
5. **Clear Function Names** - get_system_info(), get_uptime(), etc.

## API Documentation

### `GET /`

Returns service info, system info, runtime, request details, and endpoints list.

```bash
curl http://localhost:5000/
```

### `GET /health`

Returns health status and uptime.

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

### Error Responses

```bash
curl http://localhost:5000/something
# {"error": "Not Found", "message": "Endpoint does not exist"}
```

## Testing Evidence

Screenshots in `docs/screenshots/`:

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

## Challenges & Solutions

1. **Function name conflict** — Named the route handler `get_system_info()` which conflicted with the imported function from `services.system_info`. When calling `get_system_info()` inside the route handler, Python was calling the route handler itself instead of the imported function, causing recursion errors. Fixed by importing the entire module as `import services.system_info as system_info_service` and accessing functions via `system_info_service.get_system_info()`.

2. **Timezone method call error** — Used `timezone.utc.tzname()` without arguments, but `tzname()` method requires a datetime object as parameter. This caused `TypeError: timezone.tzname() takes exactly one argument (0 given)`. Fixed by calling `tzname()` on a datetime object: `datetime.now(timezone.utc).tzname()`.

## GitHub Community

\*Starring repositories helps with discovery and bookmarking — it signals project quality to the community and encourages maintainers. Following developers builds professional connections and keeps you informed about relevant projects and industry trends.
Loading