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
2 changes: 2 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
*/__pycache__
.venv/
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
*/__pycache__
.venv/
130 changes: 130 additions & 0 deletions API.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
# API and CLI Spec

## Running the application

### With Docker Compose

1. **Build and run the services:**
```bash
docker compose --profile dev up --build
```

2. **Initialize the database:**
Automatic database migration via Atlas cli

## API Specification

### Create a server

- **Endpoint:** `POST /servers`
- **Request Body:**
```json
{
"hostname": "string",
"ip_address": "string",
"state": "string"
}
```
- **Response:**
```json
{
"id": "integer",
"hostname": "string",
"ip_address": "string",
"state": "string"
}
```

### List all servers

- **Endpoint:** `GET /servers`
- **Response:**
```json
[
{
"id": "integer",
"hostname": "string",
"ip_address": "string",
"state": "string"
}
]
```

### Get one server

- **Endpoint:** `GET /servers/{id}`
- **Response:**
```json
{
"id": "integer",
"hostname": "string",
"ip_address": "string",
"state": "string"
}
```

### Update a server

- **Endpoint:** `PUT /servers/{id}`
- **Request Body:**
```json
{
"hostname": "string",
"ip_address": "string",
"state": "string"
}
```
- **Response:**
```json
{
"id": "integer",
"hostname": "string",
"ip_address": "string",
"state": "string"
}
```

### Delete a server

- **Endpoint:** `DELETE /servers/{id}`
- **Response:**
```json
{
"id": "integer",
"hostname": "string",
"ip_address": "string",
"state": "string"
}
```

## CLI Specification

### Create a server

```bash
python cli/main.py create <hostname> <ip_address> <state>
```

### List all servers

```bash
python cli/main.py list
```

### Get one server

```bash
python cli/main.py get <id>
```

### Update a server

```bash
python cli/main.py update <id> <hostname> <ip_address> <state>
```

### Delete a server

```bash
python cli/main.py delete <id>
```
13 changes: 13 additions & 0 deletions DEV.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
To run database

docker compose run db -d

Run service

docker compose run --profile dev up

Run tests

docker compose run --rm test-runner

Cli dockumentstion
159 changes: 159 additions & 0 deletions DOC.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
# Developer Documentation

This document provides instructions for setting up the development environment, running the application, and using the command-line interface (CLI).

## 🚀 Getting Started

### Prerequisites

- [Docker](https://docs.docker.com/get-docker/)
- [Docker Compose](https://docs.docker.com/compose/install/)

### Environment Setup

1. **Clone the repository:**

```sh
git clone <repository-url>
cd hiring-challenge-devops-python
```

2. **Build and start the services:**

Use Docker Compose to build the images and start the API, database, and other services. The `--profile dev` flag is used to activate the development-related services.

```sh
docker compose up db -d
docker compose --profile dev up --build -d
```

3. **Apply database migrations:**

Should run atomatically

4. **Run test:**

```sh
docker compose run --build --rm test-runner
```

## CLI Usage

The CLI provides a convenient way to interact with the server inventory management system.

### `create`

Create a new server.

**Usage:**

```sh
python3 cli/main.py create <HOSTNAME> <IP_ADDRESS> <STATE>
```

**Arguments:**

| Argument | Description | Required |
| :----------- | :---------------------------------------- | :------- |
| `HOSTNAME` | Server hostname | Yes |
| `IP_ADDRESS` | Server IP address | Yes |
| `STATE` | Server state (active, offline, retired) | Yes |

**Example:**

```sh
python3 cli/main.py create "web-server-01" "192.168.1.10" "active"
```

### `list`

List all servers in the inventory.

**Usage:**

```sh
python3 cli/main.py list
```

**Example:**

```sh
python3 cli/main.py list
```

### `get`

Retrieve a single server by its ID.

**Usage:**

```sh
python3 cli/main.py get <SERVER_ID>
```

**Arguments:**

| Argument | Description | Required |
| :---------- | :----------------------- | :------- |
| `SERVER_ID` | ID of the server to retrieve | Yes |

**Example:**

```sh
python3 cli/main.py get 1
```

### `update`

Update a server's details.

**Usage:**

```sh
python3 cli/main.py update <SERVER_ID> <HOSTNAME> <IP_ADDRESS> <STATE>
```

**Arguments:**

| Argument | Description | Required |
| :----------- | :-------------------------------------- | :------- |
| `SERVER_ID` | ID of the server to update | Yes |
| `HOSTNAME` | New server hostname | Yes |
| `IP_ADDRESS` | New server IP address | Yes |
| `STATE` | New server state (active, offline, retired) | Yes |

**Example:**

```sh
python3 cli/main.py update 1 "updated-server-01" "192.168.1.11" "offline"
```

### `delete`

Delete a server from the inventory.

**Usage:**

```sh
python3 cli/main.py delete <SERVER_ID>
```

**Arguments:**

| Argument | Description | Required |
| :---------- | :--------------------- | :------- |
| `SERVER_ID` | ID of the server to delete | Yes |

**Example:**

```sh
python3 cli/main.py delete 1
```

## 🧪 Running Tests

To run the test suite, use the `test-runner` service. This will set up a dedicated test database, apply migrations, and then execute the tests.

```sh
docker-compose --profile test up --build --abort-on-container-exit
```
40 changes: 40 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
FROM python:3.12-slim

ARG INSTALL_ATLAS="false"
RUN if [ "$INSTALL_ATLAS" = "true" ]; then \
apt-get update && apt-get install -y curl && \
curl -sSf https://atlasgo.sh | sh; \
rm -rf /var/lib/apt/lists/*; \
fi

WORKDIR /code
RUN pip install uv

# Create a non-root user for the application
RUN useradd --create-home application

# Create a directory for the virtual environment and set ownership
RUN mkdir /venv && chown application:application /venv

# Switch to the new user
USER application
WORKDIR /home/application

# Set up the virtual environment
ENV VIRTUAL_ENV=/venv
ENV WORKON_HOME=/venv
RUN python -m venv $VIRTUAL_ENV
ENV PATH="$VIRTUAL_ENV/bin:$PATH"

# Copy dependency files and install them into the virtual environment
WORKDIR /code
COPY ./pyproject.toml ./uv.lock ./
RUN uv sync --no-cache --active

# Copy the rest of the application code
COPY ./app /code/app
COPY ./cli /code/cli
COPY ./migrations /code/migrations

# Run the application
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]
Empty file added app/__init__.py
Empty file.
15 changes: 15 additions & 0 deletions app/database.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import os
import psycopg2
from psycopg2.extras import RealDictCursor
from dotenv import load_dotenv

load_dotenv()

def get_db_connection():
conn = psycopg2.connect(
host=os.getenv("POSTGRES_HOST", "localhost"),
database=os.getenv("POSTGRES_DB", "inventory"),
user=os.getenv("POSTGRES_USER", "user"),
password=os.getenv("POSTGRES_PASSWORD", "password")
)
return conn
Loading