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
10 changes: 10 additions & 0 deletions docker-tools/nightly-nightly-scavenger-drone-sim/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
FROM python:3.9-slim-buster

WORKDIR /app

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

COPY src/drone_sim.py .

CMD ["python", "drone_sim.py"]
103 changes: 103 additions & 0 deletions docker-tools/nightly-nightly-scavenger-drone-sim/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
# Nightly Scavenger Drone Simulator (`nightly-scavenger-drone-sim`)

A whimsical-yet-useful Dockerized utility that simulates a scavenger drone reporting on discovered resources and anomalies in a post-apocalyptic wasteland. This tool can be used for:

- **Testing Monitoring Systems**: Generate a stream of simulated resource reports and anomalies to test how your monitoring or log aggregation systems handle varied data.
- **Demonstrating Docker**: A simple, self-contained example of packaging a Python script into a Docker image.
- **Whimsical Background Process**: Run it as a cron job on a server to add a touch of post-apocalyptic flavor to your system logs.

## Classifier

`docker-tools`

## How it Works

The utility runs a Python script inside a Docker container. The script randomly generates a JSON report containing:
- A timestamp and a drone ID (configurable via `DRONE_ID` environment variable).
- A simulated location (sector and grid).
- A list of "findings," which can include:
- Discovered resources (e.g., "scrap metal", "purified water") with quantities.
- Detected anomalies (e.g., "temporal distortion", "unidentified signal source").
- A "no significant findings" status if nothing else is found.

The report is printed to standard output (stdout) as a JSON string.

## Usage

### 1. Build the Docker Image

Navigate to the `nightly-scavenger-drone-sim` directory and build the Docker image:

```bash
docker build -t scavenger-drone-sim .
```

### 2. Run the Simulator

You can run the simulator to generate a single report:

```bash
docker run scavenger-drone-sim
```

Example output:

```json
{
"timestamp": "2023-10-27T10:30:00+00:00",
"drone_id": "DRONE-ALPHA-7",
"location": {
"sector": "Alpha",
"grid": "42-88"
},
"findings": [
{
"type": "resource",
"item": "scrap metal",
"quantity": 5
},
{
"type": "anomaly",
"description": "temporal distortion detected"
}
]
}
```

### 3. Customize Drone ID

Set the `DRONE_ID` environment variable to give your drone a unique identifier:

```bash
docker run -e DRONE_ID="DRONE-BETA-9000" scavenger-drone-sim
```

### 4. Continuous Reporting (Example)

To simulate continuous reporting, you could run it in a loop (e.g., every 5 seconds):

```bash
while true; do docker run scavenger-drone-sim; sleep 5; done
```

Or, for a more robust solution, integrate it into a `cron` job or a container orchestration platform.

## Development and Testing

### Prerequisites

- Python 3.9+
- Docker

### Running Tests

The tests ensure that the report generation logic works as expected under various conditions, using mocks to make random outcomes deterministic.

```bash
# Ensure you are in the nightly-scavenger-drone-sim directory
python -m unittest tests/test_drone_sim.py
```

## Contributing

Feel free to enhance the drone's findings, add more resource types, or introduce new anomaly categories!
47 changes: 47 additions & 0 deletions docker-tools/nightly-nightly-scavenger-drone-sim/src/drone_sim.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import random
import json
import datetime
import os

def generate_report():
resources = [
"scrap metal", "diluted fuel", "purified water", "ancient tech parts",
"mutated flora samples", "intact pre-fall rations", "medical supplies"
]
anomalies = [
"temporal distortion detected", "faint energy signature", "unidentified signal source",
"strange atmospheric phenomenon", "ghostly echo of a bygone era", "localized gravity fluctuation"
]

report = {
"timestamp": datetime.datetime.now(datetime.timezone.utc).isoformat(),
"drone_id": os.getenv("DRONE_ID", "DRONE-ALPHA-7"),
"location": {
"sector": random.choice(["Alpha", "Beta", "Gamma", "Delta"]),
"grid": f"{random.randint(1, 99):02d}-{random.randint(1, 99):02d}"
},
"findings": []
}

# Decide if we find resources
if random.random() < 0.7: # 70% chance of finding resources
num_resources = random.randint(1, 3)
for _ in range(num_resources):
resource = random.choice(resources)
quantity = random.randint(1, 10)
report["findings"].append({"type": "resource", "item": resource, "quantity": quantity})

# Decide if we detect anomalies
if random.random() < 0.3: # 30% chance of detecting anomalies
num_anomalies = random.randint(1, 2)
for _ in range(num_anomalies):
anomaly = random.choice(anomalies)
report["findings"].append({"type": "anomaly", "description": anomaly})

if not report["findings"]:
report["findings"].append({"type": "status", "description": "No significant findings, routine patrol."})

return json.dumps(report, indent=2)

if __name__ == "__main__":
print(generate_report())
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# No external dependencies for this simple script
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
import unittest
from unittest.mock import patch, MagicMock
import json
import datetime
import os
from drone_sim import generate_report # Assuming drone_sim.py is in the same directory for testing

class TestDroneSim(unittest.TestCase):

@patch('drone_sim.datetime')
@patch('drone_sim.random.random')
@patch('drone_sim.random.choice')
@patch('drone_sim.random.randint')
@patch.dict(os.environ, {'DRONE_ID': 'TEST-DRONE-001'}) # Mock rationale: Set a consistent DRONE_ID for tests
def test_report_with_resources_and_anomalies(self, mock_randint, mock_choice, mock_random, mock_datetime):
# Mock rationale: Ensure deterministic timestamp
mock_datetime.datetime.now.return_value = datetime.datetime(2023, 10, 27, 10, 30, 0, tzinfo=datetime.timezone.utc)
mock_datetime.timezone.utc = datetime.timezone.utc

# Mock rationale: Control random outcomes for resources and anomalies
# random.random() calls:
# 1. For resources (should be < 0.7)
# 2. For anomalies (should be < 0.3)
mock_random.side_effect = [0.5, 0.2] # 0.5 < 0.7 (resources), 0.2 < 0.3 (anomalies)

# Mock rationale: Control random choices for items and descriptions
mock_choice.side_effect = [
"Alpha", "01-01", # Location
"scrap metal", # Resource 1
"diluted fuel", # Resource 2
"temporal distortion detected", # Anomaly 1
"unidentified signal source" # Anomaly 2
]

# Mock rationale: Control random integers for quantities and counts
# random.randint() calls:
# 1. num_resources (1-3) -> 2
# 2. quantity for scrap metal (1-10) -> 5
# 3. quantity for diluted fuel (1-10) -> 3
# 4. num_anomalies (1-2) -> 2
mock_randint.side_effect = [2, 5, 3, 2]

report_str = generate_report()
report = json.loads(report_str)

self.assertEqual(report["drone_id"], "TEST-DRONE-001")
self.assertEqual(report["timestamp"], "2023-10-27T10:30:00+00:00")
self.assertEqual(report["location"]["sector"], "Alpha")
self.assertEqual(report["location"]["grid"], "01-01")
self.assertEqual(len(report["findings"]), 4) # 2 resources + 2 anomalies

self.assertEqual(report["findings"][0]["type"], "resource")
self.assertEqual(report["findings"][0]["item"], "scrap metal")
self.assertEqual(report["findings"][0]["quantity"], 5)

self.assertEqual(report["findings"][1]["type"], "resource")
self.assertEqual(report["findings"][1]["item"], "diluted fuel")
self.assertEqual(report["findings"][1]["quantity"], 3)

self.assertEqual(report["findings"][2]["type"], "anomaly")
self.assertEqual(report["findings"][2]["description"], "temporal distortion detected")

self.assertEqual(report["findings"][3]["type"], "anomaly")
self.assertEqual(report["findings"][3]["description"], "unidentified signal source")

@patch('drone_sim.datetime')
@patch('drone_sim.random.random')
@patch('drone_sim.random.choice')
@patch('drone_sim.random.randint')
@patch.dict(os.environ, {'DRONE_ID': 'TEST-DRONE-002'}) # Mock rationale: Set a consistent DRONE_ID for tests
def test_report_no_findings(self, mock_randint, mock_choice, mock_random, mock_datetime):
# Mock rationale: Ensure deterministic timestamp
mock_datetime.datetime.now.return_value = datetime.datetime(2023, 10, 27, 11, 0, 0, tzinfo=datetime.timezone.utc)
mock_datetime.timezone.utc = datetime.timezone.utc

# Mock rationale: Control random outcomes to ensure no resources or anomalies are found
mock_random.side_effect = [0.8, 0.4] # 0.8 > 0.7 (no resources), 0.4 > 0.3 (no anomalies)

# Mock rationale: Control random choices for location
mock_choice.side_effect = ["Beta", "02-02"]

# Mock rationale: No randint calls expected for findings, but location grid uses it.
mock_randint.side_effect = [2, 2] # For location grid 02-02

report_str = generate_report()
report = json.loads(report_str)

self.assertEqual(report["drone_id"], "TEST-DRONE-002")
self.assertEqual(report["timestamp"], "2023-10-27T11:00:00+00:00")
self.assertEqual(report["location"]["sector"], "Beta")
self.assertEqual(report["location"]["grid"], "02-02")
self.assertEqual(len(report["findings"]), 1)
self.assertEqual(report["findings"][0]["type"], "status")
self.assertEqual(report["findings"][0]["description"], "No significant findings, routine patrol.")

@patch('drone_sim.datetime')
@patch('drone_sim.random.random')
@patch('drone_sim.random.choice')
@patch('drone_sim.random.randint')
@patch.dict(os.environ, {'DRONE_ID': 'TEST-DRONE-003'}) # Mock rationale: Set a consistent DRONE_ID for tests
def test_report_only_resources(self, mock_randint, mock_choice, mock_random, mock_datetime):
# Mock rationale: Ensure deterministic timestamp
mock_datetime.datetime.now.return_value = datetime.datetime(2023, 10, 27, 12, 0, 0, tzinfo=datetime.timezone.utc)
mock_datetime.timezone.utc = datetime.timezone.utc

# Mock rationale: Control random outcomes to ensure resources are found, but no anomalies
mock_random.side_effect = [0.1, 0.5] # 0.1 < 0.7 (resources), 0.5 > 0.3 (no anomalies)

# Mock rationale: Control random choices for location and resources
mock_choice.side_effect = [
"Gamma", "03-03", # Location
"purified water" # Resource 1
]

# Mock rationale: Control random integers for quantities and counts
mock_randint.side_effect = [1, 7, 3, 3] # num_resources=1, quantity=7, location grid 03-03

report_str = generate_report()
report = json.loads(report_str)

self.assertEqual(report["drone_id"], "TEST-DRONE-003")
self.assertEqual(report["timestamp"], "2023-10-27T12:00:00+00:00")
self.assertEqual(report["location"]["sector"], "Gamma")
self.assertEqual(report["location"]["grid"], "03-03")
self.assertEqual(len(report["findings"]), 1)

self.assertEqual(report["findings"][0]["type"], "resource")
self.assertEqual(report["findings"][0]["item"], "purified water")
self.assertEqual(report["findings"][0]["quantity"], 7)

if __name__ == '__main__':
unittest.main()
Loading