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: 9 additions & 1 deletion GUI/src/vast/desktop/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,16 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
libxcb-xinerama0 libxcb-cursor0 libxcb-keysyms1 libxcb-render-util0 \
libxcb-randr0 && rm -rf /var/lib/apt/lists/*

# # ───────── optional CA certs ─────────
RUN if [ -d certs ]; then \
echo "Copying certs directory..."; \
tar -cf - certs | tar -xf -; \
else \
echo "No certs directory, skipping copy."; \
fi
Comment on lines +25 to +30
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic: The tar command syntax is incorrect - it creates a tar archive from certs and immediately extracts it to the same location, which serves no purpose. If the intent is to conditionally copy certs, use COPY --from=build-context . . or mount the directory. What is the intended source and destination for the certs directory copy?


# ───────── optional CA certs ─────────
COPY certs /app/certs

RUN if [ -d ./certs ] && [ "$(ls ./certs/*.crt 2>/dev/null)" ]; then \
echo "Configuring NetFree certificates..."; \
cp ./certs/*.crt /usr/local/share/ca-certificates/; \
Expand Down
8 changes: 7 additions & 1 deletion GUI/src/vast/gateway/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,13 @@ WORKDIR /app
# ARG USE_NETFREE=true

RUN apt-get update && apt-get install -y --no-install-recommends ca-certificates curl && rm -rf /var/lib/apt/lists/*
COPY certs /app/certs
RUN if [ -d certs ]; then \
echo "Copying certs directory..."; \
tar -cf - certs | tar -xf -; \
else \
echo "No certs directory, skipping copy."; \
fi

# System CA + add NetFree certs
RUN if [ "$USE_NETFREE" = "true" ] && [ -d ./certs ] && [ "$(ls ./certs/*.crt 2>/dev/null)" ]; then \
echo "Configuring NetFree certificates..."; \
Expand Down
8 changes: 7 additions & 1 deletion GUI/src/vast/runner/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,13 @@ WORKDIR /app
ARG USE_NETFREE=true

RUN apt-get update && apt-get install -y --no-install-recommends ca-certificates curl && rm -rf /var/lib/apt/lists/*
COPY certs /app/certs
RUN if [ -d certs ]; then \
echo "Copying certs directory..."; \
tar -cf - certs | tar -xf -; \
else \
echo "No certs directory, skipping copy."; \
fi
Comment on lines +9 to +14
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: The tar pipe operation tar -cf - certs | tar -xf - is unnecessarily complex for copying a directory. This could be simplified to cp -r certs ./ or cp -r certs /app/.

Suggested change
RUN if [ -d certs ]; then \
echo "Copying certs directory..."; \
tar -cf - certs | tar -xf -; \
else \
echo "No certs directory, skipping copy."; \
fi
RUN if [ -d certs ]; then \
echo "Copying certs directory..."; \
cp -r certs ./; \
else \
echo "No certs directory, skipping copy."; \
fi

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!


# System CA + add NetFree certs
RUN if [ "$USE_NETFREE" = "true" ] && [ -d ./certs ] && [ "$(ls ./certs/*.crt 2>/dev/null)" ]; then \
echo "Configuring NetFree certificates..."; \
Expand Down
16 changes: 13 additions & 3 deletions GUI/src/vast/services/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,10 +1,20 @@
FROM python:3.11-slim
ENV PYTHONDONTWRITEBYTECODE=1 PYTHONUNBUFFERED=1
WORKDIR /app
# # System CA + NetFree

# System CA + NetFree

RUN apt-get update && apt-get install -y --no-install-recommends ca-certificates curl && rm -rf /var/lib/apt/lists/*
COPY certs/*.crt /usr/local/share/ca-certificates/
RUN update-ca-certificates || true

RUN if [ -d certs ] && [ "$(ls certs/*.crt 2>/dev/null)" ]; then \
echo "Installing local certificates..."; \
cp certs/*.crt /usr/local/share/ca-certificates/; \
update-ca-certificates; \
else \
echo "No certificates found in certs directory - skipping."; \
fi


ENV SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt \
REQUESTS_CA_BUNDLE=/etc/ssl/certs/ca-certificates.crt \
PIP_CERT=/etc/ssl/certs/ca-certificates.crt
Expand Down
35 changes: 35 additions & 0 deletions RelDB/build_tables/schema.sql
Original file line number Diff line number Diff line change
Expand Up @@ -511,6 +511,41 @@ CREATE TABLE IF NOT EXISTS public.aerial_images_complete_metadata (
created_at TIMESTAMP DEFAULT NOW()
);

-- === fruit_defect_predictions ===
CREATE TABLE IF NOT EXISTS public.fruit_defect_predictions (
id BIGSERIAL PRIMARY KEY,
ts TIMESTAMPTZ NOT NULL DEFAULT now(),
bucket TEXT NOT NULL,
object_key TEXT NOT NULL,
image_uri TEXT, -- s3://bucket/key
label TEXT NOT NULL CHECK (label IN ('ok','defect')),
score DOUBLE PRECISION,
confidence DOUBLE PRECISION,
latency_ms_http INTEGER,
latency_ms_model INTEGER,
device_id TEXT,
idem_key TEXT, -- Idempotency-Key (for UPSERT)
corr_id TEXT, -- X-Correlation-ID
extra JSONB NOT NULL DEFAULT '{}'::jsonb
);
CREATE UNIQUE INDEX IF NOT EXISTS ux_fd_idem
ON public.fruit_defect_predictions (idem_key);
CREATE INDEX IF NOT EXISTS ix_fd_ts
ON public.fruit_defect_predictions (ts);
CREATE INDEX IF NOT EXISTS ix_fd_label
ON public.fruit_defect_predictions (label);
CREATE INDEX IF NOT EXISTS ix_fd_bucket_object
ON public.fruit_defect_predictions (bucket, object_key);
CREATE INDEX IF NOT EXISTS ix_fd_device
ON public.fruit_defect_predictions (device_id);
CREATE INDEX IF NOT EXISTS ix_fd_extra_gin
ON public.fruit_defect_predictions USING GIN (extra);
CREATE INDEX IF NOT EXISTS ix_fd_defects
ON public.fruit_defect_predictions (ts) WHERE label = 'defect';




CREATE INDEX IF NOT EXISTS idx_aerial_metadata_device_id
ON public.aerial_images_complete_metadata (device_id);

Expand Down
172 changes: 163 additions & 9 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ networks:
ag_cloud:
name: ag_cloud
driver: bridge
flink-net:
driver: bridge

# --------------------------
# Volumes
Expand Down Expand Up @@ -851,7 +853,7 @@ services:
container_name: flink_writer_db
environment:
- KAFKA_BROKERS=kafka:9092
- TOPICS=sensor_zone_stats,sensor_anomalies,image_new_security_connections,alerts,image_new_aerial_connections,aerial_images_metadata,aerial_image_object_detections,aerial_image_anomaly_detections,aerial_images_complete_metadata,field_polygons,aerial_image_segmentation,sound_new_sounds_connections,sound_new_plants_connections
- TOPICS=sensor_zone_stats,sensor_anomalies,image_new_security_connections,alerts,image_new_aerial_connections,aerial_images_metadata,aerial_image_object_detections,aerial_image_anomaly_detections,aerial_images_complete_metadata,aerial_image_segmentation,sound_new_sounds_connections,sound_new_plants_connections
- DB_API_BASE=http://db_api_service:8001
- DB_API_AUTH_MODE=service
- DB_API_SERVICE_NAME=flink-writer-db
Expand Down Expand Up @@ -879,9 +881,9 @@ services:
FLINK_PROPERTIES=
jobmanager.rpc.address: flink-jobmanager
parallelism.default: 2
taskmanager.numberOfTaskSlots: 2
taskmanager.numberOfTaskSlots: 4
jobmanager.memory.process.size: 1600m
taskmanager.memory.process.size: 1728m
taskmanager.memory.process.size: 2048m
s3.endpoint: http://minio-hot:9000
s3.path.style.access: true
s3.access.key: minioadmin
Expand Down Expand Up @@ -954,22 +956,22 @@ services:
networks: [ ag_cloud ]
environment:
- KAFKA_BOOTSTRAP=kafka:9092
- INPUT_TOPIC=imagery.new.fruit
- INPUT_TOPIC=inference.dispatched.camera
- TEAM=fruit
- HTTP_URL=http://fruit-inference-http:8004/infer_json
- DLQ_TOPIC=dlq.inference.http
- GROUP_ID=http-dispatcher-fruit
- PARALLELISM=2
- PYFLINK_CLIENT_EXECUTABLE=/usr/bin/python3
volumes:
- ./streaming/flink/jobs:/opt/flink/jobs:ro
- ./streaming/flink/jobs:/opt/flink/jobs
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: Jobs directory volume changed from read-only to read-write - ensure this doesn't introduce security risks

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

- ./streaming/flink/connectors/flink-connector-kafka-3.2.0-1.18.jar:/opt/flink/lib/flink-connector-kafka-3.2.0-1.18.jar:ro
- ./streaming/flink/connectors/flink-sql-connector-kafka-3.2.0-1.18.jar:/opt/flink/lib/flink-sql-connector-kafka-3.2.0-1.18.jar:ro
- ./streaming/flink/connectors/flink-json-1.18.1.jar:/opt/flink/lib/flink-json-1.18.1.jar:ro
- ./streaming/flink/connectors/kafka-clients-3.2.3.jar:/opt/flink/lib/kafka-clients-3.2.3.jar:ro
- ./streaming/flink/connectors/lz4-java-1.8.0.jar:/opt/flink/lib/lz4-java-1.8.0.jar:ro
- ./streaming/flink/connectors/snappy-java-1.1.10.5.jar:/opt/flink/lib/snappy-java-1.1.10.5.jar:ro
command: [ "bash", "-lc", "set -e; echo 'Waiting for JobManager to accept commands...'; until /opt/flink/bin/flink list --jobmanager flink-jobmanager:8081 >/dev/null 2>&1; do echo 'still waiting...'; sleep 3; done; echo 'JobManager is ready!'; /opt/flink/bin/flink run -Dpython.client.executable=/usr/bin/python3 -Dpython.executable=/usr/bin/python3 -Dpipeline.jars=file:///opt/flink/lib/flink-connector-kafka-3.2.0-1.18.jar,file:///opt/flink/lib/flink-sql-connector-kafka-3.2.0-1.18.jar,file:///opt/flink/lib/flink-json-1.18.1.jar --jobmanager flink-jobmanager:8081 --detached --python /opt/flink/jobs/http_dispatcher.py -- --bootstrap kafka:9092 --input-topic imagery.new.fruit --team fruit --http-url http://fruit-inference-http:8004/infer_json --group-id http-dispatcher-fruit --dlq-topic dlq.inference.http; tail -f /dev/null" ]
command: [ "bash", "-lc", "set -e; echo 'Waiting for JobManager to accept commands...'; until /opt/flink/bin/flink list --jobmanager flink-jobmanager:8081 >/dev/null 2>&1; do echo 'still waiting...'; sleep 3; done; echo 'JobManager is ready!'; /opt/flink/bin/flink run -Dpython.client.executable=/usr/bin/python3 -Dpython.executable=/usr/bin/python3 -Dpipeline.jars=file:///opt/flink/lib/flink-connector-kafka-3.2.0-1.18.jar,file:///opt/flink/lib/flink-sql-connector-kafka-3.2.0-1.18.jar,file:///opt/flink/lib/flink-json-1.18.1.jar --jobmanager flink-jobmanager:8081 --detached --python /opt/flink/jobs/http_dispatcher.py -- --bootstrap kafka:9092 --input-topic inference.dispatched.camera --team fruit --http-url http://fruit-inference-http:8004/infer_json --group-id http-dispatcher-fruit --dlq-topic dlq.inference.http; tail -f /dev/null" ]
restart: always

flink-dispatcher-camera:
Expand All @@ -982,17 +984,17 @@ services:
networks: [ag_cloud]
environment:
- KAFKA_BOOTSTRAP=kafka:9092
- INPUT_TOPIC=imagery.new.camera
- INPUT_TOPIC=image.new.fruits
- TEAM=camera
- HTTP_URL=http://camera-inference-http:8004/infer_json
- DLQ_TOPIC=dlq.inference.http
- GROUP_ID=http-dispatcher-camera
- PARALLELISM=2
- PYFLINK_CLIENT_EXECUTABLE=/usr/bin/python3
volumes:
- ./streaming/flink/jobs:/opt/flink/jobs:ro
- ./streaming/flink/jobs:/opt/flink/jobs
- ./streaming/flink/connectors:/opt/flink/lib/connectors:ro
command: [ "bash", "-lc", "set -e; echo 'Waiting for JobManager to accept commands...'; until /opt/flink/bin/flink list --jobmanager flink-jobmanager:8081 >/dev/null 2>&1; do echo 'still waiting...'; sleep 3; done; echo 'JobManager is ready!'; /opt/flink/bin/flink run -Dpython.client.executable=/usr/bin/python3 -Dpython.executable=/usr/bin/python3 -Dpipeline.jars=file:///opt/flink/lib/connectors/flink-connector-kafka-3.2.0-1.18.jar,file:///opt/flink/lib/connectors/flink-sql-connector-kafka-3.2.0-1.18.jar,file:///opt/flink/lib/connectors/flink-json-1.18.1.jar --jobmanager flink-jobmanager:8081 --detached --python /opt/flink/jobs/http_dispatcher.py -- --bootstrap kafka:9092 --input-topic imagery.new.camera --team camera --http-url http://camera-inference-http:8004/infer_json --group-id http-dispatcher-camera --dlq-topic dlq.inference.http; tail -f /dev/null" ]
command: [ "bash", "-lc", "set -e; echo 'Waiting for JobManager to accept commands...'; until /opt/flink/bin/flink list --jobmanager flink-jobmanager:8081 >/dev/null 2>&1; do echo 'still waiting...'; sleep 3; done; echo 'JobManager is ready!'; /opt/flink/bin/flink run -Dpython.client.executable=/usr/bin/python3 -Dpython.executable=/usr/bin/python3 -Dpipeline.jars=file:///opt/flink/lib/connectors/flink-connector-kafka-3.2.0-1.18.jar,file:///opt/flink/lib/connectors/flink-sql-connector-kafka-3.2.0-1.18.jar,file:///opt/flink/lib/connectors/flink-json-1.18.1.jar --jobmanager flink-jobmanager:8081 --detached --python /opt/flink/jobs/http_dispatcher.py -- --bootstrap kafka:9092 --input-topic image.new.fruits --team camera --http-url http://camera-inference-http:8004/infer_json --group-id http-dispatcher-camera --dlq-topic dlq.inference.http; tail -f /dev/null" ]
restart: always

flink-alerts-job:
Expand Down Expand Up @@ -1181,3 +1183,155 @@ services:
FLINK_PYTHON: /opt/venv/bin/python
networks:
- ag_cloud


# --------------------------
# Flink Air Processing
# --------------------------
air-jobmanager:
build:
context: ./services/air
dockerfile: Dockerfile.flink
container_name: air-jobmanager
command: jobmanager
ports:
- "8085:8081"
environment:
- JOB_MANAGER_RPC_ADDRESS=air-jobmanager
- KAFKA_BROKERS=kafka:9092
- IN_TOPIC=image.new.aerial
- KAFKA_GROUP_ID=flink-air-device-pipeline
networks:
- flink-net
- ag_cloud
restart: unless-stopped

air-taskmanager:
build:
context: ./services/air
dockerfile: Dockerfile.flink
container_name: air-taskmanager
command: taskmanager -D taskmanager.numberOfTaskSlots=4
depends_on:
air-jobmanager:
condition: service_started
infer-api:
condition: service_healthy
anomaly-api:
condition: service_healthy
segmentation-api:
condition: service_healthy
environment:
- JOB_MANAGER_RPC_ADDRESS=air-jobmanager
- KAFKA_BROKERS=kafka:9092
- IN_TOPIC=image.new.aerial
- OUT_TOPIC_OBJECT=aerial_image_object_detections
- OUT_TOPIC_ANOMALY=aerial_image_anomaly_detections
- OUT_TOPIC_SEGMENTATION=aerial_image_segmentation
- taskmanager.numberOfTaskSlots=4
- KAFKA_GROUP_ID=flink-air-device-pipeline
- SEGMENTATION_URL=http://segmentation-api:8500/infer
- INFER_URL=http://infer-api:8000/infer
- ANOMALY_URL=http://anomaly-api:8010/predict
- INFER_CONF=0.25
- INFER_IOU=0.45
- MINIO_ENDPOINT=minio-hot:9000
- MINIO_ACCESS_KEY=minioadmin
- MINIO_SECRET_KEY=minioadmin123
networks:
- flink-net
- ag_cloud
restart: unless-stopped

infer-api:
build:
context: ./services/air/object_detection_api
dockerfile: Dockerfile.infer
container_name: infer-api
environment:
- WEIGHTS_PATH=/app/best.pt
volumes:
- ./services/air/object_detection_api/best.pt:/app/best.pt:ro
healthcheck:
test: ["CMD", "curl", "-sf", "http://localhost:8000/health"]
interval: 10s
timeout: 3s
retries: 15
networks:
- flink-net
- ag_cloud

anomaly-api:
build:
context: ./services/air/anomaly_detection_api
dockerfile: Dockerfile.anomaly
container_name: anomaly-api
environment:
- MODEL_PATH=/app/models/best.pt
ports:
- "8020:8010"
healthcheck:
test: ["CMD", "curl", "-sf", "http://localhost:8010/health"]
interval: 10s
timeout: 3s
retries: 15
networks:
- flink-net
- ag_cloud

segmentation-api:
build:
context: ./services/air/segmentation_api
dockerfile: dockerfile.segmentation
container_name: segmentation-api
environment:
- MODEL_PATH=/app/model/best_model.pth
ports:
- "8500:8500"
volumes:
- ./services/air/segmentation_api/model:/app/model:ro
- ./services/air/segmentation_api/certs:/usr/local/share/ca-certificates/netfree:ro
healthcheck:
test: ["CMD", "curl", "-sf", "http://localhost:8500/health"]
interval: 10s
timeout: 3s
retries: 10
networks:
- flink-net
- ag_cloud

air-submit:
build:
context: ./services/air
dockerfile: Dockerfile.flink
depends_on:
air-jobmanager:
condition: service_started
command: >
bash -c "
sleep 10 &&
flink run -m air-jobmanager:8081 -py /opt/app/job.py"

fruit-defect-sink:
build:
context: ./services/fruit_defect_sink
dockerfile: Dockerfile
environment:
- KAFKA_BOOTSTRAP=kafka:9092
- INPUT_TOPIC=inference.dispatched.fruit
- ALERTS_TOPIC=alerts
- GROUP_ID=fruit-defect-sink
- AUTO_OFFSET_RESET=earliest
- MAX_POLL_INTERVAL_MS=900000
- SESSION_TIMEOUT_MS=45000
- HEARTBEAT_INTERVAL_MS=3000
- PGHOST=postgres
- PGPORT=5432
- PGDATABASE=missions_db
- PGUSER=missions_user
- PGPASSWORD=pg123
depends_on:
kafka: { condition: service_healthy }
postgres: { condition: service_healthy }
networks: [ ag_cloud ]
restart: unless-stopped
1 change: 1 addition & 0 deletions mqtt_and_kafka/kafka/kafka-files/create-topics.sh
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ TOPICS=(
sound_new_plants_connections
sound_new_sounds_connections

inference.dispatched.fruit
inference.dispatched.sounds
dlq.inference.http
)
Expand Down
26 changes: 18 additions & 8 deletions services/API-notifications/src/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,24 @@ WORKDIR /app

COPY requirements.txt .

COPY certs /app/certs

RUN apt-get update && \
apt-get install -y ca-certificates && \
cp /app/certs/*.crt /usr/local/share/ca-certificates/ && \
update-ca-certificates && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*
RUN if [ -d certs ]; then \
echo "Copying certs directory..."; \
tar -cf - certs | tar -xf -; \
Comment on lines +7 to +9
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic: The tar -cf - certs | tar -xf - command is redundant - it copies certs to the same location. This should be COPY certs ./certs or similar.

Suggested change
RUN if [ -d certs ]; then \
echo "Copying certs directory..."; \
tar -cf - certs | tar -xf -; \
COPY certs ./certs

else \
echo "No certs directory, skipping copy."; \
fi
Comment on lines +7 to +12
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic: The tar command here will fail if the certs directory doesn't exist in the build context. The conditional check happens inside the container, but the certs directory needs to exist in the Docker build context for this to work.



RUN apt-get update && apt-get install -y ca-certificates && \
if [ "$USE_NETFREE" = "true" ] && [ -d ./certs ] && [ "$(ls ./certs/*.crt 2>/dev/null)" ]; then \
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

syntax: $(ls ./certs/*.crt 2>/dev/null) will expand to filenames, not a boolean. Use ls ./certs/*.crt >/dev/null 2>&1 instead.

Suggested change
if [ "$USE_NETFREE" = "true" ] && [ -d ./certs ] && [ "$(ls ./certs/*.crt 2>/dev/null)" ]; then \
if [ "$USE_NETFREE" = "true" ] && [ -d ./certs ] && ls ./certs/*.crt >/dev/null 2>&1; then \

echo "Configuring NetFree certificates..."; \
cp ./certs/*.crt /usr/local/share/ca-certificates/; \
update-ca-certificates; \
else \
echo "Skipping certificate configuration (USE_NETFREE=$USE_NETFREE)"; \
fi && \
apt-get clean && rm -rf /var/lib/apt/lists/*


ENV REQUESTS_CA_BUNDLE=/etc/ssl/certs/ca-certificates.crt
ENV SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt
Expand Down
Loading