A RESTful API built with FastAPI for managing servers across multiple datacenters
- Create, read, update, and delete servers
- Manage server configurations as JSON
- Link servers to datacenters
- Comprehensive API documentation with Swagger UI
- Health check endpoint
- Python 3.8+
- PostgreSQL 12+
- pip (Python package manager)
git clone https://github.com/krasnoshchok/server-management-api.git
cd server-management-apipython -m venv venv
source venv/bin/activate # On Windows: venv\Scripts\activatepip install -r requirements.txtCreate a new database:
createdb server_managementRun the SQL schema file to create tables:
psql server_management < sql/schema.sqlThis will create the following tables:
datacenter- Stores datacenter informationswitch- Network switch informationserver- Server inventoryswitch_to_server- Many-to-many relationship between switches and servers
The schema file also includes sample data for testing.
Create a .env file in the project root:
# Database
DB_HOST=localhost
DB_NAME=server_management
DB_USER=postgres
DB_PASSWORD=your_password_here
DB_PORT=5432
# Logging
LOG_LEVEL=INFO
LOG_FILE=logs/app.logStart the development server:
uvicorn app.main:app --reloadThe API will be available at http://localhost:8000
A Dockerfile and docker-compose.yml (now located in the docker/ subdirectory) are provided to containerize the service and a PostgreSQL instance.
If you prefer to run the application on a local Kubernetes cluster you can use minikube.
-
Install dependencies via Homebrew:
brew install hyperkit docker brew install minikube
dockeris used as the driver by default; hyperkit is optional. -
Start the cluster using the Docker driver:
minikube start --driver=docker
-
(Optional) Enable addons such as the dashboard or ingress:
minikube addons enable dashboard minikube addons enable ingress
Because Terraform’s Docker provider may not always match the API version used by your Docker daemon (especially when using minikube), we recommend building the image manually instead of letting Terraform do it.
# either point your shell at Minikube's daemon and run docker directly:
eval $(minikube docker-env)
docker build -f docker/Dockerfile -t server-management-api:latest .
# undo the environment change when done
eval $(minikube docker-env -u)or use the minikube helper which avoids changing your shell:
minikube image build -f docker/Dockerfile -t server-management-api:latest .Once the image exists in the cluster, run terraform apply to create/update the
Kubernetes resources. If you prefer to use a remote registry instead, tag and
push the image there and update the image field in the Terraform config
accordingly.
Manifests live in the k8s/ directory and include the API deployment,
Postgres deployment/service, and a NodePort service for the API.
kubectl apply -f k8s/The application will start and expose itself on port 30080 of the minikube node. You can access it from the host with:
curl http://$(minikube ip):30080/healthor use port‑forwarding:
kubectl port-forward svc/server-api 8000:8000Once the Postgres pod is ready run the schema script as before:
kubectl exec -i deploy/postgres -- \
psql -U postgres -d server_management < sql/schema.sqlkubectl delete -f k8s/
minikube stopThe k8s/ directory can be extended with ConfigMaps, secrets or a
Job to seed the database automatically.
You can fully describe the Kubernetes resources (API deployment, database deployment,
and services) using Terraform so the same configuration can be applied on any machine
with access to a cluster. The terraform/ subdirectory contains the complete setup.
- Install Terraform (v1.5+ recommended).
- A running Kubernetes cluster (e.g. minikube).
kubectlconfigured to point at your target cluster.
eval $(minikube docker-env)
docker build -f docker/Dockerfile -t server-management-api:latest .
eval $(minikube docker-env -u) # restore previous envor use the minikube helper:
minikube image build -f docker/Dockerfile -t server-management-api:latest .cd terraform
terraform init # download providers
terraform plan -out=tfplan # review changes
terraform apply tfplan # create API + database resourcesThis creates:
server-apideployment (FastAPI service)postgresdeployment (database)server-apiservice (NodePort on port 30080)postgresservice (ClusterIP on port 5432)
kubectl exec -i deploy/postgres -- \
psql -U postgres -d server_management < sql/schema.sql# Option A: Port-forward the service
kubectl port-forward svc/server-api 8000:8000
# Then visit http://localhost:8000 or http://localhost:8000/docs
# Option B: Use the NodePort directly (if networking allows)
curl http://localhost:30080/healthVariables are defined in variables.tf. Common operations:
# View current outputs
terraform output
# Update infrastructure after code changes
terraform plan -out=tfplan && terraform apply tfplan
# Destroy all resources
terraform destroyThe Terraform setup is minimal but can be extended with:
- PersistentVolumes for database persistence
- Secrets for sensitive configuration
- Ingress rules for cleaner HTTP access
- RBAC and NetworkPolicies for security
- HPA (Horizontal Pod Autoscaler) for scaling
For cloud deployments (AWS, GCP, Azure) you can replace the postgres deployment
with a managed database service and push the API image to a container registry
(ECR, GCR, ACR, etc.).
# Dockerfile is in docker/ so pass -f or change directory
docker build -f docker/Dockerfile -t server-management-api .Run the container with the database connection specified via environment variables:
# use the same network or provide a reachable Postgres
docker run -e DB_HOST=host.docker.internal \
-e DB_NAME=server_management \
-e DB_USER=postgres \
-e DB_PASSWORD=your_password \
-e DB_PORT=5432 \
-p 8000:8000 \
server-management-api# bring up both database and api; compose file is in docker/
# from project root:
docker-compose -f docker/docker-compose.yml up --build # run from project root- API available at
http://localhost:8000 - PostgreSQL listens on
localhost:5432with credentials shown indocker-compose.yml.
Logs are mounted to ./logs so the host can inspect app.log.
After the database container starts you still need to create the tables. You can either run the sql/schema.sql from the host against localhost:5432 or exec into the container:
# once compose is running
docker exec -i $(docker-compose ps -q db) psql -U postgres -d server_management < sql/schema.sqlThis will populate the schema and sample data as described above.
Once the server is running, access the interactive API documentation:
- Swagger UI: http://localhost:8000/docs
- ReDoc: http://localhost:8000/redoc
- GET
/health- Verify the service is running
- GET
/servers/- Retrieve all servers - GET
/servers/{server_id}- Retrieve a specific server by ID - POST
/servers/- Create a new server - PUT
/servers/{server_id}- Update an existing server - DELETE
/servers/{server_id}- Delete a server
curl -X POST "http://localhost:8000/servers/" \
-H "Content-Type: application/json" \
-d '{
"hostname": "webserver.local.lan",
"configuration": {"cpu_cores": 8, "ram_gb": 32},
"datacenter_id": 1
}'Important
Resource Limits Enforced
The server configuration strictly enforces hardware limits. Invalid values will result in a 400 Bad Request error.
cpu_cores: Must be between 1 and 128ram_gb: Must be between 1 and 4096
Get first 100 servers (default):
curl -X GET "http://localhost:8000/servers/"Get first 10 servers:
curl -X GET "http://localhost:8000/servers/?limit=10"Get servers 11-20 (skip first 10, return next 10):
curl -X GET "http://localhost:8000/servers/?skip=10&limit=10"Get servers 101-200:
curl -X GET "http://localhost:8000/servers/?skip=100&limit=100"curl -X GET "http://localhost:8000/servers/1"curl -X PUT "http://localhost:8000/servers/1" \
-H "Content-Type: application/json" \
-d '{
"hostname": "updated-webserver.local.lan",
"configuration": {"cpu_cores": 16, "ram_gb": 64}
}'curl -X DELETE "http://localhost:8000/servers/1"server-management-api/
│
├── app/
│ ├── __init__.py
│ ├── main.py # FastAPI application entry point
│ ├── database.py # Database connection management
│ ├── models.py # Pydantic models for validation
│ ├── constants.py # Database table constants
│ ├── logging_config.py # Logging configuration
│ └── routers/
│ ├── __init__.py
│ └── servers.py # Server endpoint definitions
│
├── logs/
│ └── app.log # Application logs (gitignored)
│
├── sql/
│ └── schema.sql # Database schema and sample data
│
├── .env # Environment variables (gitignored)
├── .gitignore
├── requirements.txt # Python dependencies
└── README.md
- Used context managers (
@asynccontextmanager) for automatic connection cleanup RealDictCursorreturns query results as dictionaries for easier JSON serialization- Manual transaction management (commit/rollback) for better control
- Direct SQL queries using
asyncpgas per requirements - Parameterized queries to prevent SQL injection
- Dynamic query building for partial updates
- RESTful conventions (GET, POST, PUT, DELETE)
- Proper HTTP status codes (200, 201, 204, 400, 404)
- Comprehensive error handling with descriptive messages
- Request/response validation using Pydantic models
- Server
configurationfield stored as JSONB in PostgreSQL - Flexible schema allows different configurations per server
- Validated as dictionary type in Pydantic models
The API returns standard HTTP error codes:
200 OK- Successful GET request201 Created- Successful POST request204 No Content- Successful DELETE request400 Bad Request- Invalid input or missing required fields404 Not Found- Resource not found500 Internal Server Error- Server-side error
You can test the API using:
- Swagger UI at http://localhost:8000/docs (interactive testing)
- curl commands (see examples above)
- Postman or similar API clients
- Python requests library
The application includes comprehensive logging for monitoring and debugging.
Configure logging in your .env file:
# Logging (optional)
LOG_LEVEL=INFO # Options: DEBUG, INFO, WARNING, ERROR, CRITICAL
LOG_FILE=logs/app.log # Leave empty to disable file logging- DEBUG: Detailed information for diagnosing problems (shows all query details)
- INFO: General informational messages (default, recommended for production)
- WARNING: Warning messages for unexpected events
- ERROR: Error messages for serious problems
- CRITICAL: Critical errors that may cause the application to stop
Logs are written to two locations:
- Console/Terminal - All logs appear in the terminal where you run uvicorn
- Log File - Persistent logs saved to
logs/app.log(if configured)
View logs in real-time:
tail -f logs/app.log2024-05-28 15:30:52 - INFO - Fetching servers with skip=0, limit=100
2024-05-28 15:30:52 - INFO - Retrieved 3 servers
2024-05-28 15:31:10 - INFO - Creating server: hostname=newserver.local, datacenter_id=1
2024-05-28 15:31:10 - INFO - Created server with id=4, hostname=newserver.local
2024-05-28 15:31:45 - WARNING - Server with id=99 not found
The following files are ignored by git (see .gitignore):
.env- Contains sensitive database credentialslogs/- Log files can grow large and contain sensitive data__pycache__/and*.pyc- Python bytecode files
To run in development mode with auto-reload:
uvicorn app.main:app --reload --host 0.0.0.0 --port 8000- The API follows PEP 8 style guidelines
- All endpoints include comprehensive docstrings
- Foreign key constraints are validated before operations
- Timestamps are automatically managed by PostgreSQL
- The
modified_atfield is updated on every server update