/data and /tmp standarization

This commit is contained in:
Adam Outler
2025-11-04 22:26:35 +00:00
parent 90a07c61eb
commit 5b871865db
250 changed files with 7462 additions and 4940 deletions

View File

@@ -18,7 +18,7 @@ This directory contains docker-compose configurations for different test scenari
The `mount-tests/` subdirectory contains 24 docker-compose configurations that test all possible mount scenarios for each path that NetAlertX monitors:
- **6 paths**: `/app/db`, `/app/config`, `/app/api`, `/app/log`, `/services/run`, `/services/config/nginx/conf.active`
- **6 paths**: `/data/db`, `/data/config`, `/tmp/api`, `/tmp/log`, `/tmp/run`, `/tmp/nginx/active-config`
- **4 scenarios per path**: `no-mount`, `ramdisk`, `mounted`, `unwritable`
- **Total**: 24 comprehensive test configurations

View File

@@ -13,13 +13,8 @@ services:
volumes:
- type: volume
source: netalertx_config
target: /app/config
read_only: false
- type: volume
source: netalertx_db
target: /app/db
source: netalertx_data
target: /data
read_only: false
- type: bind
@@ -45,5 +40,4 @@ services:
max-file: "3"
volumes:
netalertx_config:
netalertx_db:
netalertx_data:

View File

@@ -17,13 +17,8 @@ services:
volumes:
- type: volume
source: netalertx_config
target: /app/config
read_only: false
- type: volume
source: netalertx_db
target: /app/db
source: netalertx_data
target: /data
read_only: false
- type: bind
@@ -51,5 +46,4 @@ services:
restart: unless-stopped
volumes:
netalertx_config:
netalertx_db:
netalertx_data:

View File

@@ -17,13 +17,8 @@ services:
volumes:
- type: volume
source: netalertx_config
target: /app/config
read_only: false
- type: volume
source: netalertx_db
target: /app/db
source: netalertx_data
target: /data
read_only: false
- type: bind
@@ -31,17 +26,8 @@ services:
target: /etc/localtime
read_only: true
# Tempfs mounts for writable directories in a read-only container and improve system performance
# tmpfs mount aligns with simplified runtime layout
tmpfs:
# Speed up logging
- "/app/log:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
# Speed up API access
- "/app/api:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,sync,noatime,nodiratime"
# Required for customization of the nginx listen addr/port
- "/services/config/nginx/conf.active:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
# Required for nginx and php
- "/services/run:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
# Required by php for session save
- "/tmp:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
environment:
@@ -64,5 +50,4 @@ services:
restart: unless-stopped
volumes:
netalertx_config:
netalertx_db:
netalertx_data:

View File

@@ -22,31 +22,19 @@ services:
APP_CONF_OVERRIDE: 20212
ALWAYS_FRESH_INSTALL: true
NETALERTX_DEBUG: 0
SYSTEM_SERVICES_ACTIVE_CONFIG: /services/config/nginx/conf.active
SYSTEM_SERVICES_ACTIVE_CONFIG: /tmp/nginx/active-config
volumes:
- type: volume
source: netalertx_db
target: /app/db
read_only: false
- type: volume
source: netalertx_config
target: /app/config
source: test_netalertx_data
target: /data
read_only: false
- type: volume
source: test_system_services_active_config
target: /services/config/nginx/conf.active
target: /tmp/nginx/active-config
read_only: false
tmpfs:
- "/app/api:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
- "/app/log:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
- "/services/run:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
- "/tmp:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
volumes:
netalertx_config:
netalertx_db:
test_netalertx_db:
test_netalertx_config:
test_netalertx_api:
test_netalertx_log:
test_system_services_run:
test_netalertx_data:
test_system_services_active_config:

View File

@@ -22,27 +22,14 @@ services:
APP_CONF_OVERRIDE: 20212
ALWAYS_FRESH_INSTALL: true
NETALERTX_DEBUG: 0
SYSTEM_SERVICES_ACTIVE_CONFIG: /services/config/nginx/conf.active
SYSTEM_SERVICES_ACTIVE_CONFIG: /tmp/nginx/active-config
volumes:
- type: volume
source: netalertx_db
target: /app/db
read_only: false
- type: volume
source: netalertx_config
target: /app/config
source: test_netalertx_data
target: /data
read_only: false
tmpfs:
- "/app/api:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
- "/app/log:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
- "/services/run:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
- "/tmp:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
volumes:
netalertx_config:
netalertx_db:
test_netalertx_db:
test_netalertx_config:
test_netalertx_api:
test_netalertx_log:
test_system_services_run:
test_system_services_active_config:
test_netalertx_data:

View File

@@ -22,28 +22,14 @@ services:
APP_CONF_OVERRIDE: 20212
ALWAYS_FRESH_INSTALL: true
NETALERTX_DEBUG: 0
SYSTEM_SERVICES_ACTIVE_CONFIG: /services/config/nginx/conf.active
SYSTEM_SERVICES_ACTIVE_CONFIG: /tmp/nginx/active-config
volumes:
- type: volume
source: netalertx_db
target: /app/db
read_only: false
- type: volume
source: netalertx_config
target: /app/config
source: test_netalertx_data
target: /data
read_only: false
tmpfs:
- "/app/api:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
- "/app/log:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
- "/services/run:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
- "/services/config/nginx/conf.active:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
- "/tmp:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
volumes:
netalertx_config:
netalertx_db:
test_netalertx_db:
test_netalertx_config:
test_netalertx_api:
test_netalertx_log:
test_system_services_run:
test_system_services_active_config:
test_netalertx_data:

View File

@@ -22,31 +22,19 @@ services:
APP_CONF_OVERRIDE: 20212
ALWAYS_FRESH_INSTALL: true
NETALERTX_DEBUG: 0
SYSTEM_SERVICES_ACTIVE_CONFIG: /services/config/nginx/conf.active
SYSTEM_SERVICES_ACTIVE_CONFIG: /tmp/nginx/active-config
volumes:
- type: volume
source: netalertx_db
target: /app/db
read_only: false
- type: volume
source: netalertx_config
target: /app/config
source: test_netalertx_data
target: /data
read_only: false
- type: volume
source: test_system_services_active_config
target: /services/config/nginx/conf.active
target: /tmp/nginx/active-config
read_only: true
tmpfs:
- "/app/api:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
- "/app/log:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
- "/services/run:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
- "/tmp:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
volumes:
netalertx_config:
netalertx_db:
test_netalertx_db:
test_netalertx_config:
test_netalertx_api:
test_netalertx_log:
test_system_services_run:
test_netalertx_data:
test_system_services_active_config:

View File

@@ -22,25 +22,25 @@ services:
APP_CONF_OVERRIDE: 20212
ALWAYS_FRESH_INSTALL: true
NETALERTX_DEBUG: 0
NETALERTX_API: /app/api
NETALERTX_API: /tmp/api
volumes:
- type: volume
source: netalertx_db
target: /app/db
target: /data/db
read_only: false
- type: volume
source: netalertx_config
target: /app/config
target: /data/config
read_only: false
- type: volume
source: test_netalertx_api
target: /app/api
target: /tmp/api
read_only: false
tmpfs:
- "/app/log:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
- "/services/run:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
- "/services/config/nginx/conf.active:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
- "/tmp/log:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
- "/tmp/run:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
- "/tmp/nginx/active-config:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
volumes:
netalertx_config:
netalertx_db:

View File

@@ -22,21 +22,21 @@ services:
APP_CONF_OVERRIDE: 20212
ALWAYS_FRESH_INSTALL: true
NETALERTX_DEBUG: 0
NETALERTX_API: /app/api
NETALERTX_API: /tmp/api
volumes:
- type: volume
source: netalertx_db
target: /app/db
target: /data/db
read_only: false
- type: volume
source: netalertx_config
target: /app/config
target: /data/config
read_only: false
tmpfs:
- "/app/log:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
- "/services/run:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
- "/services/config/nginx/conf.active:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
- "/tmp/log:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
- "/tmp/run:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
- "/tmp/nginx/active-config:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
volumes:
netalertx_config:
netalertx_db:

View File

@@ -22,28 +22,14 @@ services:
APP_CONF_OVERRIDE: 20212
ALWAYS_FRESH_INSTALL: true
NETALERTX_DEBUG: 0
NETALERTX_API: /app/api
NETALERTX_API: /tmp/api
volumes:
- type: volume
source: netalertx_db
target: /app/db
read_only: false
- type: volume
source: netalertx_config
target: /app/config
source: test_netalertx_data
target: /data
read_only: false
tmpfs:
- "/app/api:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
- "/app/log:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
- "/services/run:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
- "/services/config/nginx/conf.active:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
- "/tmp:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
volumes:
netalertx_config:
netalertx_db:
test_netalertx_db:
test_netalertx_config:
test_netalertx_api:
test_netalertx_log:
test_system_services_run:
test_system_services_active_config:
test_netalertx_data:

View File

@@ -22,25 +22,25 @@ services:
APP_CONF_OVERRIDE: 20212
ALWAYS_FRESH_INSTALL: true
NETALERTX_DEBUG: 0
NETALERTX_API: /app/api
NETALERTX_API: /tmp/api
volumes:
- type: volume
source: netalertx_db
target: /app/db
target: /data/db
read_only: false
- type: volume
source: netalertx_config
target: /app/config
target: /data/config
read_only: false
- type: volume
source: test_netalertx_api
target: /app/api
target: /tmp/api
read_only: true
tmpfs:
- "/app/log:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
- "/services/run:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
- "/services/config/nginx/conf.active:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
- "/tmp/log:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
- "/tmp/run:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
- "/tmp/nginx/active-config:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
volumes:
netalertx_config:
netalertx_db:

View File

@@ -22,28 +22,14 @@ services:
APP_CONF_OVERRIDE: 20212
ALWAYS_FRESH_INSTALL: true
NETALERTX_DEBUG: 0
NETALERTX_CONFIG: /app/config
NETALERTX_CONFIG: /data/config
volumes:
- type: volume
source: netalertx_db
target: /app/db
read_only: false
- type: volume
source: test_netalertx_config
target: /app/config
source: test_netalertx_data
target: /data
read_only: false
tmpfs:
- "/app/api:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
- "/app/log:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
- "/services/run:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
- "/services/config/nginx/conf.active:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
- "/tmp:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
volumes:
netalertx_config:
netalertx_db:
test_netalertx_db:
test_netalertx_config:
test_netalertx_api:
test_netalertx_log:
test_system_services_run:
test_system_services_active_config:
test_netalertx_data:

View File

@@ -22,18 +22,18 @@ services:
APP_CONF_OVERRIDE: 20212
ALWAYS_FRESH_INSTALL: true
NETALERTX_DEBUG: 0
NETALERTX_CONFIG: /app/config
NETALERTX_CONFIG: /data/config
volumes:
- type: volume
source: netalertx_db
target: /app/db
target: /data/db
read_only: false
tmpfs:
- "/app/api:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
- "/app/log:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
- "/services/run:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
- "/services/config/nginx/conf.active:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
- "/tmp/api:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
- "/tmp/log:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
- "/tmp/run:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
- "/tmp/nginx/active-config:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
volumes:
netalertx_config:
netalertx_db:

View File

@@ -22,19 +22,19 @@ services:
APP_CONF_OVERRIDE: 20212
ALWAYS_FRESH_INSTALL: true
NETALERTX_DEBUG: 0
NETALERTX_CONFIG: /app/config
NETALERTX_CONFIG: /data/config
volumes:
- type: volume
source: netalertx_db
target: /app/db
target: /data/db
read_only: false
tmpfs:
- "/app/config:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
- "/app/api:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
- "/app/log:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
- "/services/run:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
- "/services/config/nginx/conf.active:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
- "/data/config:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
- "/tmp/api:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
- "/tmp/log:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
- "/tmp/run:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
- "/tmp/nginx/active-config:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
volumes:
netalertx_config:
netalertx_db:

View File

@@ -22,22 +22,22 @@ services:
APP_CONF_OVERRIDE: 20212
ALWAYS_FRESH_INSTALL: true
NETALERTX_DEBUG: 0
NETALERTX_CONFIG: /app/config
NETALERTX_CONFIG: /data/config
volumes:
- type: volume
source: netalertx_db
target: /app/db
target: /data/db
read_only: false
- type: volume
source: test_netalertx_config
target: /app/config
target: /data/config
read_only: true
tmpfs:
- "/app/api:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
- "/app/log:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
- "/services/run:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
- "/services/config/nginx/conf.active:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
- "/tmp/api:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
- "/tmp/log:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
- "/tmp/run:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
- "/tmp/nginx/active-config:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
volumes:
netalertx_config:
netalertx_db:

View File

@@ -22,28 +22,14 @@ services:
APP_CONF_OVERRIDE: 20212
ALWAYS_FRESH_INSTALL: true
NETALERTX_DEBUG: 0
NETALERTX_DB: /app/db
NETALERTX_DB: /data/db
volumes:
- type: volume
source: test_netalertx_db
target: /app/db
read_only: false
- type: volume
source: netalertx_config
target: /app/config
source: test_netalertx_data
target: /data
read_only: false
tmpfs:
- "/app/api:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
- "/app/log:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
- "/services/run:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
- "/services/config/nginx/conf.active:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
- "/tmp:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
volumes:
netalertx_config:
netalertx_db:
test_netalertx_db:
test_netalertx_config:
test_netalertx_api:
test_netalertx_log:
test_system_services_run:
test_system_services_active_config:
test_netalertx_data:

View File

@@ -22,18 +22,18 @@ services:
APP_CONF_OVERRIDE: 20212
ALWAYS_FRESH_INSTALL: true
NETALERTX_DEBUG: 0
NETALERTX_DB: /app/db
NETALERTX_DB: /data/db
volumes:
- type: volume
source: netalertx_config
target: /app/config
target: /data/config
read_only: false
tmpfs:
- "/app/api:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
- "/app/log:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
- "/services/run:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
- "/services/config/nginx/conf.active:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
- "/tmp/api:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
- "/tmp/log:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
- "/tmp/run:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
- "/tmp/nginx/active-config:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
volumes:
netalertx_config:
netalertx_db:

View File

@@ -22,19 +22,19 @@ services:
APP_CONF_OVERRIDE: 20212
ALWAYS_FRESH_INSTALL: true
NETALERTX_DEBUG: 0
NETALERTX_DB: /app/db
NETALERTX_DB: /data/db
volumes:
- type: volume
source: netalertx_config
target: /app/config
target: /data/config
read_only: false
tmpfs:
- "/app/db:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
- "/app/api:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
- "/app/log:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
- "/services/run:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
- "/services/config/nginx/conf.active:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
- "/data/db:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
- "/tmp/api:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
- "/tmp/log:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
- "/tmp/run:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
- "/tmp/nginx/active-config:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
volumes:
netalertx_config:
netalertx_db:

View File

@@ -22,22 +22,22 @@ services:
APP_CONF_OVERRIDE: 20212
ALWAYS_FRESH_INSTALL: true
NETALERTX_DEBUG: 0
NETALERTX_DB: /app/db
NETALERTX_DB: /data/db
volumes:
- type: volume
source: test_netalertx_db
target: /app/db
target: /data/db
read_only: true
- type: volume
source: netalertx_config
target: /app/config
target: /data/config
read_only: false
tmpfs:
- "/app/api:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
- "/app/log:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
- "/services/run:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
- "/services/config/nginx/conf.active:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
- "/tmp/api:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
- "/tmp/log:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
- "/tmp/run:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
- "/tmp/nginx/active-config:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
volumes:
netalertx_config:
netalertx_db:

View File

@@ -22,25 +22,25 @@ services:
APP_CONF_OVERRIDE: 20212
ALWAYS_FRESH_INSTALL: true
NETALERTX_DEBUG: 0
NETALERTX_LOG: /app/log
NETALERTX_LOG: /tmp/log
volumes:
- type: volume
source: netalertx_db
target: /app/db
target: /data/db
read_only: false
- type: volume
source: netalertx_config
target: /app/config
target: /data/config
read_only: false
- type: volume
source: test_netalertx_log
target: /app/log
target: /tmp/log
read_only: false
tmpfs:
- "/app/api:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
- "/services/run:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
- "/services/config/nginx/conf.active:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
- "/tmp/api:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
- "/tmp/run:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
- "/tmp/nginx/active-config:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
volumes:
netalertx_config:
netalertx_db:

View File

@@ -22,21 +22,21 @@ services:
APP_CONF_OVERRIDE: 20212
ALWAYS_FRESH_INSTALL: true
NETALERTX_DEBUG: 0
NETALERTX_LOG: /app/log
NETALERTX_LOG: /tmp/log
volumes:
- type: volume
source: netalertx_db
target: /app/db
target: /data/db
read_only: false
- type: volume
source: netalertx_config
target: /app/config
target: /data/config
read_only: false
tmpfs:
- "/app/api:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
- "/services/run:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
- "/services/config/nginx/conf.active:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
- "/tmp/api:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
- "/tmp/run:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
- "/tmp/nginx/active-config:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
volumes:
netalertx_config:
netalertx_db:

View File

@@ -22,28 +22,14 @@ services:
APP_CONF_OVERRIDE: 20212
ALWAYS_FRESH_INSTALL: true
NETALERTX_DEBUG: 0
NETALERTX_LOG: /app/log
NETALERTX_LOG: /tmp/log
volumes:
- type: volume
source: netalertx_db
target: /app/db
read_only: false
- type: volume
source: netalertx_config
target: /app/config
source: test_netalertx_data
target: /data
read_only: false
tmpfs:
- "/app/api:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
- "/app/log:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
- "/services/run:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
- "/services/config/nginx/conf.active:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
- "/tmp:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
volumes:
netalertx_config:
netalertx_db:
test_netalertx_db:
test_netalertx_config:
test_netalertx_api:
test_netalertx_log:
test_system_services_run:
test_system_services_active_config:
test_netalertx_data:

View File

@@ -22,25 +22,25 @@ services:
APP_CONF_OVERRIDE: 20212
ALWAYS_FRESH_INSTALL: true
NETALERTX_DEBUG: 0
NETALERTX_LOG: /app/log
NETALERTX_LOG: /tmp/log
volumes:
- type: volume
source: netalertx_db
target: /app/db
target: /data/db
read_only: false
- type: volume
source: netalertx_config
target: /app/config
target: /data/config
read_only: false
- type: volume
source: test_netalertx_log
target: /app/log
target: /tmp/log
read_only: true
tmpfs:
- "/app/api:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
- "/services/run:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
- "/services/config/nginx/conf.active:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
- "/tmp/api:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
- "/tmp/run:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
- "/tmp/nginx/active-config:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
volumes:
netalertx_config:
netalertx_db:

View File

@@ -22,25 +22,25 @@ services:
APP_CONF_OVERRIDE: 20212
ALWAYS_FRESH_INSTALL: true
NETALERTX_DEBUG: 0
SYSTEM_SERVICES_RUN: /services/run
SYSTEM_SERVICES_RUN: /tmp/run
volumes:
- type: volume
source: netalertx_db
target: /app/db
target: /data/db
read_only: false
- type: volume
source: netalertx_config
target: /app/config
target: /data/config
read_only: false
- type: volume
source: test_system_services_run
target: /services/run
target: /tmp/run
read_only: false
tmpfs:
- "/app/api:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
- "/app/log:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
- "/services/config/nginx/conf.active:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
- "/tmp/api:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
- "/tmp/log:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
- "/tmp/nginx/active-config:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
volumes:
netalertx_config:
netalertx_db:

View File

@@ -22,21 +22,21 @@ services:
APP_CONF_OVERRIDE: 20212
ALWAYS_FRESH_INSTALL: true
NETALERTX_DEBUG: 0
SYSTEM_SERVICES_RUN: /services/run
SYSTEM_SERVICES_RUN: /tmp/run
volumes:
- type: volume
source: netalertx_db
target: /app/db
target: /data/db
read_only: false
- type: volume
source: netalertx_config
target: /app/config
target: /data/config
read_only: false
tmpfs:
- "/app/api:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
- "/app/log:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
- "/services/config/nginx/conf.active:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
- "/tmp/api:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
- "/tmp/log:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
- "/tmp/nginx/active-config:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
volumes:
netalertx_config:
netalertx_db:

View File

@@ -22,28 +22,14 @@ services:
APP_CONF_OVERRIDE: 20212
ALWAYS_FRESH_INSTALL: true
NETALERTX_DEBUG: 0
SYSTEM_SERVICES_RUN: /services/run
SYSTEM_SERVICES_RUN: /tmp/run
volumes:
- type: volume
source: netalertx_db
target: /app/db
read_only: false
- type: volume
source: netalertx_config
target: /app/config
source: test_netalertx_data
target: /data
read_only: false
tmpfs:
- "/app/api:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
- "/app/log:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
- "/services/run:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
- "/services/config/nginx/conf.active:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
- "/tmp:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
volumes:
netalertx_config:
netalertx_db:
test_netalertx_db:
test_netalertx_config:
test_netalertx_api:
test_netalertx_log:
test_system_services_run:
test_system_services_active_config:
test_netalertx_data:

View File

@@ -22,25 +22,25 @@ services:
APP_CONF_OVERRIDE: 20212
ALWAYS_FRESH_INSTALL: true
NETALERTX_DEBUG: 0
SYSTEM_SERVICES_RUN: /services/run
SYSTEM_SERVICES_RUN: /tmp/run
volumes:
- type: volume
source: netalertx_db
target: /app/db
target: /data/db
read_only: false
- type: volume
source: netalertx_config
target: /app/config
target: /data/config
read_only: false
- type: volume
source: test_system_services_run
target: /services/run
target: /tmp/run
read_only: true
tmpfs:
- "/app/api:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
- "/app/log:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
- "/services/config/nginx/conf.active:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
- "/tmp/api:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
- "/tmp/log:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
- "/tmp/nginx/active-config:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
volumes:
netalertx_config:
netalertx_db:

View File

@@ -8,7 +8,8 @@ Directory: /workspaces/NetAlertX/test/docker_tests/configurations
Running docker-compose up...
Attaching to netalertx-test-missing-caps
netalertx-test-missing-caps exited with code 255
==========================================
@@ -65,12 +66,13 @@ netalertx-test-readonly | https://github.com/jokob-sk/NetAlertX/blob/main/d
netalertx-test-readonly | ══════════════════════════════════════════════════════════════════════════════
netalertx-test-readonly | Container startup checks failed with exit code 1.
netalertx-test-readonly | NETALERTX_DEBUG is set to 1, will not shut down other services if one fails.
netalertx-test-readonly | NETALERTX_DEBUG is set to 1, will not shut down other services if one fails.
netalertx-test-readonly | Starting /usr/sbin/php-fpm83 -y "/services/config/php/php-fpm.conf" -F >>"/app/log/app.php_errors.log" 2>/dev/stderr &
netalertx-test-readonly | Starting /usr/sbin/php-fpm83 -y "/services/config/php/php-fpm.conf" -F >>"/tmp/log/app.php_errors.log" 2>/dev/stderr &
netalertx-test-readonly | Starting /usr/sbin/crond -c "/services/config/crond" -f -L "/tmp/log/crond.log" >>"/tmp/log/crond.log" 2>&1 &
netalertx-test-readonly | php-fpm stopped! (exit 1)
netalertx-test-readonly | Crond stopped! (exit 1)
netalertx-test-readonly | Crond stopped! (exit 1)
netalertx-test-readonly | Starting python3 -m server > /app/log/stdout.log 2> >(tee /app/log/stderr.log >&2)
netalertx-test-readonly | Starting python3 -m server > /tmp/log/stdout.log 2> >(tee /tmp/log/stderr.log >&2)
netalertx-test-readonly exited with code 0
netalertx-test-readonly | --> first run config
netalertx-test-readonly | --> first run db
netalertx-test-readonly | --> mandatory folders
@@ -101,12 +103,13 @@ netalertx-test-readonly | https://github.com/jokob-sk/NetAlertX/blob/main/d
netalertx-test-readonly | ══════════════════════════════════════════════════════════════════════════════
netalertx-test-readonly | Container startup checks failed with exit code 1.
netalertx-test-readonly | NETALERTX_DEBUG is set to 1, will not shut down other services if one fails.
netalertx-test-readonly | Container startup checks failed with exit code 1.
netalertx-test-readonly | NETALERTX_DEBUG is set to 1, will not shut down other services if one fails.
netalertx-test-readonly | Starting /usr/sbin/crond -c "/services/config/crond" -f -L "/tmp/log/crond.log" >>"/tmp/log/crond.log" 2>&1 &
netalertx-test-readonly | Starting /usr/sbin/php-fpm83 -y "/services/config/php/php-fpm.conf" -F >>"/tmp/log/app.php_errors.log" 2>/dev/stderr &
netalertx-test-readonly | Crond stopped! (exit 1)
netalertx-test-readonly | php-fpm stopped! (exit 1)
netalertx-test-readonly | Crond stopped! (exit 1)
netalertx-test-readonly | php-fpm stopped! (exit 1)
netalertx-test-readonly | Starting python3 -m server > /tmp/log/stdout.log 2> >(tee /tmp/log/stderr.log >&2)
netalertx-test-readonly exited with code 0
netalertx-test-readonly | --> first run config
netalertx-test-readonly | --> first run db
netalertx-test-readonly | --> mandatory folders
@@ -137,12 +140,13 @@ netalertx-test-readonly | https://github.com/jokob-sk/NetAlertX/blob/main/d
netalertx-test-readonly | ══════════════════════════════════════════════════════════════════════════════
netalertx-test-readonly | Container startup checks failed with exit code 1.
netalertx-test-readonly | NETALERTX_DEBUG is set to 1, will not shut down other services if one fails.
netalertx-test-readonly | ══════════════════════════════════════════════════════════════════════════════
netalertx-test-readonly | Container startup checks failed with exit code 1.
netalertx-test-readonly | Starting /usr/sbin/php-fpm83 -y "/services/config/php/php-fpm.conf" -F >>"/tmp/log/app.php_errors.log" 2>/dev/stderr &
netalertx-test-readonly | Starting /usr/sbin/crond -c "/services/config/crond" -f -L "/tmp/log/crond.log" >>"/tmp/log/crond.log" 2>&1 &
netalertx-test-readonly | Crond stopped! (exit 1)
netalertx-test-readonly | php-fpm stopped! (exit 1)
netalertx-test-readonly | Starting /usr/sbin/crond -c "/services/config/crond" -f -L "/app/log/crond.log" >>"/app/log/crond.log" 2>&1 &
netalertx-test-readonly | Crond stopped! (exit 1)
netalertx-test-readonly | Starting python3 -m server > /tmp/log/stdout.log 2> >(tee /tmp/log/stderr.log >&2)
netalertx-test-readonly exited with code 0
==========================================
@@ -207,10 +211,10 @@ netalertx-test-writable |
netalertx-test-writable | https://github.com/jokob-sk/NetAlertX/blob/main/docs/docker-troubleshooting/port-conflicts.md
netalertx-test-writable | ══════════════════════════════════════════════════════════════════════════════
netalertx-test-writable | NETALERTX_DEBUG is set to 1, will not shut down other services if one fails.
netalertx-test-writable |
netalertx-test-writable | https://github.com/jokob-sk/NetAlertX/blob/main/docs/docker-troubleshooting/port-conflicts.md
netalertx-test-writable | ══════════════════════════════════════════════════════════════════════════════
netalertx-test-writable | NETALERTX_DEBUG is set to 1, will not shut down other services if one fails.
netalertx-test-writable | Starting /usr/sbin/crond -c "/services/config/crond" -f -L "/tmp/log/crond.log" >>"/tmp/log/crond.log" 2>&1 &
netalertx-test-writable | Starting /usr/sbin/php-fpm83 -y "/services/config/php/php-fpm.conf" -F >>"/tmp/log/app.php_errors.log" 2>/dev/stderr &
netalertx-test-writable | Starting python3 -m server > /tmp/log/stdout.log 2> >(tee /tmp/log/stderr.log >&2)
netalertx-test-writable | Starting /usr/sbin/nginx -p "/tmp/run/" -c "/tmp/nginx-active-config/netalertx.conf" -g "error_log /dev/stderr; error_log /tmp/log/nginx-error.log; pid /tmp/run/nginx.pid; daemon off;" &
netalertx-test-writable | nginx stopped! (exit 1)
netalertx-test-writable | Successfully updated IEEE OUI database (111620 entries)
@@ -276,9 +280,9 @@ netalertx-test-mount-active_config_mounted |
netalertx-test-mount-active_config_mounted | https://github.com/jokob-sk/NetAlertX/blob/main/docs/docker-troubleshooting/port-conflicts.md
netalertx-test-mount-active_config_mounted | ══════════════════════════════════════════════════════════════════════════════
netalertx-test-mount-active_config_mounted | Container startup checks failed with exit code 1.
netalertx-test-mount-active_config_mounted |
netalertx-test-mount-active_config_mounted | https://github.com/jokob-sk/NetAlertX/blob/main/docs/docker-troubleshooting/port-conflicts.md
netalertx-test-mount-active_config_mounted | ══════════════════════════════════════════════════════════════════════════════
netalertx-test-mount-active_config_mounted | Starting /usr/sbin/php-fpm83 -y "/services/config/php/php-fpm.conf" -F >>"/tmp/log/app.php_errors.log" 2>/dev/stderr &
netalertx-test-mount-active_config_mounted | Starting /usr/sbin/crond -c "/services/config/crond" -f -L "/tmp/log/crond.log" >>"/tmp/log/crond.log" 2>&1 &
netalertx-test-mount-active_config_mounted | Starting python3 -m server > /tmp/log/stdout.log 2> >(tee /tmp/log/stderr.log >&2)
netalertx-test-mount-active_config_mounted | Successfully updated IEEE OUI database (111620 entries)
==========================================
@@ -343,9 +347,9 @@ netalertx-test-mount-active_config_no-mount |
netalertx-test-mount-active_config_no-mount | https://github.com/jokob-sk/NetAlertX/blob/main/docs/docker-troubleshooting/port-conflicts.md
netalertx-test-mount-active_config_no-mount | ══════════════════════════════════════════════════════════════════════════════
netalertx-test-mount-active_config_no-mount | Container startup checks failed with exit code 1.
netalertx-test-mount-active_config_no-mount |
netalertx-test-mount-active_config_no-mount | https://github.com/jokob-sk/NetAlertX/blob/main/docs/docker-troubleshooting/port-conflicts.md
netalertx-test-mount-active_config_no-mount | ══════════════════════════════════════════════════════════════════════════════
netalertx-test-mount-active_config_no-mount | Starting /usr/sbin/php-fpm83 -y "/services/config/php/php-fpm.conf" -F >>"/tmp/log/app.php_errors.log" 2>/dev/stderr &
netalertx-test-mount-active_config_no-mount | Starting /usr/sbin/crond -c "/services/config/crond" -f -L "/tmp/log/crond.log" >>"/tmp/log/crond.log" 2>&1 &
netalertx-test-mount-active_config_no-mount | Starting python3 -m server > /tmp/log/stdout.log 2> >(tee /tmp/log/stderr.log >&2)
netalertx-test-mount-active_config_no-mount | Successfully updated IEEE OUI database (111620 entries)
==========================================
@@ -407,10 +411,10 @@ netalertx-test-mount-active_config_ramdisk | may fail to start.
netalertx-test-mount-active_config_ramdisk |
netalertx-test-mount-active_config_ramdisk | https://github.com/jokob-sk/NetAlertX/blob/main/docs/docker-troubleshooting/port-conflicts.md
netalertx-test-mount-active_config_ramdisk | ══════════════════════════════════════════════════════════════════════════════
netalertx-test-mount-active_config_ramdisk | may fail to start.
netalertx-test-mount-active_config_ramdisk |
netalertx-test-mount-active_config_ramdisk | https://github.com/jokob-sk/NetAlertX/blob/main/docs/docker-troubleshooting/port-conflicts.md
netalertx-test-mount-active_config_ramdisk | ══════════════════════════════════════════════════════════════════════════════
netalertx-test-mount-active_config_ramdisk | Starting /usr/sbin/crond -c "/services/config/crond" -f -L "/tmp/log/crond.log" >>"/tmp/log/crond.log" 2>&1 &
netalertx-test-mount-active_config_ramdisk | Starting /usr/sbin/php-fpm83 -y "/services/config/php/php-fpm.conf" -F >>"/tmp/log/app.php_errors.log" 2>/dev/stderr &
netalertx-test-mount-active_config_ramdisk | Starting python3 -m server > /tmp/log/stdout.log 2> >(tee /tmp/log/stderr.log >&2)
netalertx-test-mount-active_config_ramdisk | Starting /usr/sbin/nginx -p "/tmp/run/" -c "/tmp/nginx-active-config/netalertx.conf" -g "error_log /dev/stderr; error_log /tmp/log/nginx-error.log; pid /tmp/run/nginx.pid; daemon off;" &
netalertx-test-mount-active_config_ramdisk | Successfully updated IEEE OUI database (111620 entries)
==========================================
@@ -475,9 +479,9 @@ netalertx-test-mount-active_config_unwritable |
netalertx-test-mount-active_config_unwritable | https://github.com/jokob-sk/NetAlertX/blob/main/docs/docker-troubleshooting/port-conflicts.md
netalertx-test-mount-active_config_unwritable | ══════════════════════════════════════════════════════════════════════════════
netalertx-test-mount-active_config_unwritable | Container startup checks failed with exit code 1.
netalertx-test-mount-active_config_unwritable |
netalertx-test-mount-active_config_unwritable | https://github.com/jokob-sk/NetAlertX/blob/main/docs/docker-troubleshooting/port-conflicts.md
netalertx-test-mount-active_config_unwritable | ══════════════════════════════════════════════════════════════════════════════
netalertx-test-mount-active_config_unwritable | Starting /usr/sbin/php-fpm83 -y "/services/config/php/php-fpm.conf" -F >>"/tmp/log/app.php_errors.log" 2>/dev/stderr &
netalertx-test-mount-active_config_unwritable | Starting /usr/sbin/crond -c "/services/config/crond" -f -L "/tmp/log/crond.log" >>"/tmp/log/crond.log" 2>&1 &
netalertx-test-mount-active_config_unwritable | Starting python3 -m server > /tmp/log/stdout.log 2> >(tee /tmp/log/stderr.log >&2)
netalertx-test-mount-active_config_unwritable | Successfully updated IEEE OUI database (111620 entries)
==========================================
@@ -539,10 +543,10 @@ netalertx-test-mount-api_mounted | may fail to start.
netalertx-test-mount-api_mounted |
netalertx-test-mount-api_mounted | https://github.com/jokob-sk/NetAlertX/blob/main/docs/docker-troubleshooting/port-conflicts.md
netalertx-test-mount-api_mounted | ══════════════════════════════════════════════════════════════════════════════
netalertx-test-mount-api_mounted | may fail to start.
netalertx-test-mount-api_mounted |
netalertx-test-mount-api_mounted | https://github.com/jokob-sk/NetAlertX/blob/main/docs/docker-troubleshooting/port-conflicts.md
netalertx-test-mount-api_mounted | ══════════════════════════════════════════════════════════════════════════════
netalertx-test-mount-api_mounted | Starting /usr/sbin/crond -c "/services/config/crond" -f -L "/tmp/log/crond.log" >>"/tmp/log/crond.log" 2>&1 &
netalertx-test-mount-api_mounted | Starting /usr/sbin/php-fpm83 -y "/services/config/php/php-fpm.conf" -F >>"/tmp/log/app.php_errors.log" 2>/dev/stderr &
netalertx-test-mount-api_mounted | Starting python3 -m server > /tmp/log/stdout.log 2> >(tee /tmp/log/stderr.log >&2)
netalertx-test-mount-api_mounted | Starting /usr/sbin/nginx -p "/tmp/run/" -c "/tmp/nginx-active-config/netalertx.conf" -g "error_log /dev/stderr; error_log /tmp/log/nginx-error.log; pid /tmp/run/nginx.pid; daemon off;" &
netalertx-test-mount-api_mounted | Successfully updated IEEE OUI database (111620 entries)
==========================================
@@ -604,10 +608,10 @@ netalertx-test-mount-api_no-mount | may fail to start.
netalertx-test-mount-api_no-mount |
netalertx-test-mount-api_no-mount | https://github.com/jokob-sk/NetAlertX/blob/main/docs/docker-troubleshooting/port-conflicts.md
netalertx-test-mount-api_no-mount | ══════════════════════════════════════════════════════════════════════════════
netalertx-test-mount-api_no-mount | may fail to start.
netalertx-test-mount-api_no-mount |
netalertx-test-mount-api_no-mount | https://github.com/jokob-sk/NetAlertX/blob/main/docs/docker-troubleshooting/port-conflicts.md
netalertx-test-mount-api_no-mount | ══════════════════════════════════════════════════════════════════════════════
netalertx-test-mount-api_no-mount | Starting /usr/sbin/php-fpm83 -y "/services/config/php/php-fpm.conf" -F >>"/tmp/log/app.php_errors.log" 2>/dev/stderr &
netalertx-test-mount-api_no-mount | Starting /usr/sbin/crond -c "/services/config/crond" -f -L "/tmp/log/crond.log" >>"/tmp/log/crond.log" 2>&1 &
netalertx-test-mount-api_no-mount | Starting python3 -m server > /tmp/log/stdout.log 2> >(tee /tmp/log/stderr.log >&2)
netalertx-test-mount-api_no-mount | Starting /usr/sbin/nginx -p "/tmp/run/" -c "/tmp/nginx-active-config/netalertx.conf" -g "error_log /dev/stderr; error_log /tmp/log/nginx-error.log; pid /tmp/run/nginx.pid; daemon off;" &
netalertx-test-mount-api_no-mount | Successfully updated IEEE OUI database (111620 entries)
==========================================
@@ -669,10 +673,10 @@ netalertx-test-mount-api_ramdisk | may fail to start.
netalertx-test-mount-api_ramdisk |
netalertx-test-mount-api_ramdisk | https://github.com/jokob-sk/NetAlertX/blob/main/docs/docker-troubleshooting/port-conflicts.md
netalertx-test-mount-api_ramdisk | ══════════════════════════════════════════════════════════════════════════════
netalertx-test-mount-api_ramdisk | may fail to start.
netalertx-test-mount-api_ramdisk |
netalertx-test-mount-api_ramdisk | https://github.com/jokob-sk/NetAlertX/blob/main/docs/docker-troubleshooting/port-conflicts.md
netalertx-test-mount-api_ramdisk | ══════════════════════════════════════════════════════════════════════════════
netalertx-test-mount-api_ramdisk | Starting /usr/sbin/php-fpm83 -y "/services/config/php/php-fpm.conf" -F >>"/tmp/log/app.php_errors.log" 2>/dev/stderr &
netalertx-test-mount-api_ramdisk | Starting /usr/sbin/crond -c "/services/config/crond" -f -L "/tmp/log/crond.log" >>"/tmp/log/crond.log" 2>&1 &
netalertx-test-mount-api_ramdisk | Starting python3 -m server > /tmp/log/stdout.log 2> >(tee /tmp/log/stderr.log >&2)
netalertx-test-mount-api_ramdisk | Starting /usr/sbin/nginx -p "/tmp/run/" -c "/tmp/nginx-active-config/netalertx.conf" -g "error_log /dev/stderr; error_log /tmp/log/nginx-error.log; pid /tmp/run/nginx.pid; daemon off;" &
netalertx-test-mount-api_ramdisk | Successfully updated IEEE OUI database (111620 entries)
==========================================
@@ -706,12 +710,12 @@ netalertx-test-mount-api_unwritable | --> storage permission
netalertx-test-mount-api_unwritable | --> mounts.py
netalertx-test-mount-api_unwritable | Path | Writeable | Mount | RAMDisk | Performance | DataLoss
netalertx-test-mount-api_unwritable | ------------------------------------+-----------+-------+---------+-------------+----------
netalertx-test-mount-api_unwritable | --> storage permission
netalertx-test-mount-api_unwritable | --> mounts.py
netalertx-test-mount-api_unwritable | Path | Writeable | Mount | RAMDisk | Performance | DataLoss
netalertx-test-mount-api_unwritable | ------------------------------------+-----------+-------+---------+-------------+----------
netalertx-test-mount-api_unwritable | /app/db | ✅ | ✅ | | | ✅
netalertx-test-mount-api_unwritable | /app/config | ✅ | ✅ | | | ✅
netalertx-test-mount-api_unwritable | /data/db | ✅ | ✅ | | | ✅
netalertx-test-mount-api_unwritable | /data/config | ✅ | ✅ | | | ✅
netalertx-test-mount-api_unwritable | /tmp/api | ❌ | ✅ | ❌ | ❌ | ✅
netalertx-test-mount-api_unwritable | /tmp/log | ✅ | ✅ | ✅ | ✅ | ✅
netalertx-test-mount-api_unwritable | /tmp/run | ✅ | ✅ | | | ✅
netalertx-test-mount-api_unwritable | /tmp/nginx-active-config | ✅ | ✅ | | | ✅
netalertx-test-mount-api_unwritable | --> first run config
netalertx-test-mount-api_unwritable | --> first run db
netalertx-test-mount-api_unwritable | --> mandatory folders
@@ -742,10 +746,10 @@ netalertx-test-mount-api_unwritable | may fail to start.
netalertx-test-mount-api_unwritable |
netalertx-test-mount-api_unwritable | https://github.com/jokob-sk/NetAlertX/blob/main/docs/docker-troubleshooting/port-conflicts.md
netalertx-test-mount-api_unwritable | ══════════════════════════════════════════════════════════════════════════════
netalertx-test-mount-api_unwritable | may fail to start.
netalertx-test-mount-api_unwritable |
netalertx-test-mount-api_unwritable | https://github.com/jokob-sk/NetAlertX/blob/main/docs/docker-troubleshooting/port-conflicts.md
netalertx-test-mount-api_unwritable | ══════════════════════════════════════════════════════════════════════════════
netalertx-test-mount-api_unwritable | Starting /usr/sbin/crond -c "/services/config/crond" -f -L "/tmp/log/crond.log" >>"/tmp/log/crond.log" 2>&1 &
netalertx-test-mount-api_unwritable | Starting /usr/sbin/php-fpm83 -y "/services/config/php/php-fpm.conf" -F >>"/tmp/log/app.php_errors.log" 2>/dev/stderr &
netalertx-test-mount-api_unwritable | Starting python3 -m server > /tmp/log/stdout.log 2> >(tee /tmp/log/stderr.log >&2)
netalertx-test-mount-api_unwritable | Starting /usr/sbin/nginx -p "/tmp/run/" -c "/tmp/nginx-active-config/netalertx.conf" -g "error_log /dev/stderr; error_log /tmp/log/nginx-error.log; pid /tmp/run/nginx.pid; daemon off;" &
netalertx-test-mount-api_unwritable | Successfully updated IEEE OUI database (111620 entries)
==========================================
@@ -807,10 +811,10 @@ netalertx-test-mount-config_mounted | may fail to start.
netalertx-test-mount-config_mounted |
netalertx-test-mount-config_mounted | https://github.com/jokob-sk/NetAlertX/blob/main/docs/docker-troubleshooting/port-conflicts.md
netalertx-test-mount-config_mounted | ══════════════════════════════════════════════════════════════════════════════
netalertx-test-mount-config_mounted | may fail to start.
netalertx-test-mount-config_mounted |
netalertx-test-mount-config_mounted | https://github.com/jokob-sk/NetAlertX/blob/main/docs/docker-troubleshooting/port-conflicts.md
netalertx-test-mount-config_mounted | ══════════════════════════════════════════════════════════════════════════════
netalertx-test-mount-config_mounted | Starting /usr/sbin/php-fpm83 -y "/services/config/php/php-fpm.conf" -F >>"/tmp/log/app.php_errors.log" 2>/dev/stderr &
netalertx-test-mount-config_mounted | Starting /usr/sbin/crond -c "/services/config/crond" -f -L "/tmp/log/crond.log" >>"/tmp/log/crond.log" 2>&1 &
netalertx-test-mount-config_mounted | Starting python3 -m server > /tmp/log/stdout.log 2> >(tee /tmp/log/stderr.log >&2)
netalertx-test-mount-config_mounted | Starting /usr/sbin/nginx -p "/tmp/run/" -c "/tmp/nginx-active-config/netalertx.conf" -g "error_log /dev/stderr; error_log /tmp/log/nginx-error.log; pid /tmp/run/nginx.pid; daemon off;" &
netalertx-test-mount-config_mounted | Successfully updated IEEE OUI database (111620 entries)
==========================================
@@ -844,12 +848,12 @@ netalertx-test-mount-config_no-mount | --> storage permission
netalertx-test-mount-config_no-mount | --> mounts.py
netalertx-test-mount-config_no-mount | Path | Writeable | Mount | RAMDisk | Performance | DataLoss
netalertx-test-mount-config_no-mount | ------------------------------------+-----------+-------+---------+-------------+----------
netalertx-test-mount-config_no-mount | --> storage permission
netalertx-test-mount-config_no-mount | --> mounts.py
netalertx-test-mount-config_no-mount | Path | Writeable | Mount | RAMDisk | Performance | DataLoss
netalertx-test-mount-config_no-mount | ------------------------------------+-----------+-------+---------+-------------+----------
netalertx-test-mount-config_no-mount | /app/db | ✅ | ✅ | | | ✅
netalertx-test-mount-config_no-mount | /app/config | ✅ | | | |
netalertx-test-mount-config_no-mount | /data/db | ✅ | ✅ | | | ✅
netalertx-test-mount-config_no-mount | /data/config | ✅ | ❌ | | | ❌
netalertx-test-mount-config_no-mount | /tmp/api | ✅ | ✅ | ✅ | ✅ | ✅
netalertx-test-mount-config_no-mount | /tmp/log | ✅ | ✅ | ✅ | ✅ | ✅
netalertx-test-mount-config_no-mount | /tmp/run | ✅ | ✅ | | | ✅
netalertx-test-mount-config_no-mount | /tmp/nginx-active-config | ✅ | | | |
netalertx-test-mount-config_no-mount | --> first run config
netalertx-test-mount-config_no-mount | --> first run db
netalertx-test-mount-config_no-mount | --> mandatory folders
@@ -880,10 +884,10 @@ netalertx-test-mount-config_no-mount | may fail to start.
netalertx-test-mount-config_no-mount |
netalertx-test-mount-config_no-mount | https://github.com/jokob-sk/NetAlertX/blob/main/docs/docker-troubleshooting/port-conflicts.md
netalertx-test-mount-config_no-mount | ══════════════════════════════════════════════════════════════════════════════
netalertx-test-mount-config_no-mount | may fail to start.
netalertx-test-mount-config_no-mount |
netalertx-test-mount-config_no-mount | https://github.com/jokob-sk/NetAlertX/blob/main/docs/docker-troubleshooting/port-conflicts.md
netalertx-test-mount-config_no-mount | ══════════════════════════════════════════════════════════════════════════════
netalertx-test-mount-config_no-mount | Starting /usr/sbin/php-fpm83 -y "/services/config/php/php-fpm.conf" -F >>"/tmp/log/app.php_errors.log" 2>/dev/stderr &
netalertx-test-mount-config_no-mount | Starting /usr/sbin/crond -c "/services/config/crond" -f -L "/tmp/log/crond.log" >>"/tmp/log/crond.log" 2>&1 &
netalertx-test-mount-config_no-mount | Starting python3 -m server > /tmp/log/stdout.log 2> >(tee /tmp/log/stderr.log >&2)
netalertx-test-mount-config_no-mount | Starting /usr/sbin/nginx -p "/tmp/run/" -c "/tmp/nginx-active-config/netalertx.conf" -g "error_log /dev/stderr; error_log /tmp/log/nginx-error.log; pid /tmp/run/nginx.pid; daemon off;" &
netalertx-test-mount-config_no-mount | Successfully updated IEEE OUI database (111620 entries)
==========================================
@@ -917,12 +921,12 @@ netalertx-test-mount-config_ramdisk | --> storage permission
netalertx-test-mount-config_ramdisk | --> mounts.py
netalertx-test-mount-config_ramdisk | Path | Writeable | Mount | RAMDisk | Performance | DataLoss
netalertx-test-mount-config_ramdisk | ------------------------------------+-----------+-------+---------+-------------+----------
netalertx-test-mount-config_ramdisk | --> storage permission
netalertx-test-mount-config_ramdisk | --> mounts.py
netalertx-test-mount-config_ramdisk | Path | Writeable | Mount | RAMDisk | Performance | DataLoss
netalertx-test-mount-config_ramdisk | ------------------------------------+-----------+-------+---------+-------------+----------
netalertx-test-mount-config_ramdisk | /app/db | ✅ | ✅ | | | ✅
netalertx-test-mount-config_ramdisk | /app/config | ✅ | ✅ | | |
netalertx-test-mount-config_ramdisk | /data/db | ✅ | ✅ | | | ✅
netalertx-test-mount-config_ramdisk | /data/config | ✅ | ✅ | ❌ | | ❌
netalertx-test-mount-config_ramdisk | /tmp/api | ✅ | ✅ | ✅ | ✅ | ✅
netalertx-test-mount-config_ramdisk | /tmp/log | ✅ | ✅ | ✅ | ✅ | ✅
netalertx-test-mount-config_ramdisk | /tmp/run | ✅ | ✅ | | | ✅
netalertx-test-mount-config_ramdisk | /tmp/nginx-active-config | ✅ | ✅ | | |
netalertx-test-mount-config_ramdisk | --> first run config
netalertx-test-mount-config_ramdisk | --> first run db
netalertx-test-mount-config_ramdisk | --> mandatory folders
@@ -956,10 +960,10 @@ netalertx-test-mount-config_ramdisk |
netalertx-test-mount-config_ramdisk | https://github.com/jokob-sk/NetAlertX/blob/main/docs/docker-troubleshooting/port-conflicts.md
netalertx-test-mount-config_ramdisk | ══════════════════════════════════════════════════════════════════════════════
netalertx-test-mount-config_ramdisk | Container startup checks failed with exit code 1.
netalertx-test-mount-config_ramdisk |
netalertx-test-mount-config_ramdisk | https://github.com/jokob-sk/NetAlertX/blob/main/docs/docker-troubleshooting/port-conflicts.md
netalertx-test-mount-config_ramdisk | ══════════════════════════════════════════════════════════════════════════════
netalertx-test-mount-config_ramdisk | Container startup checks failed with exit code 1.
netalertx-test-mount-config_ramdisk | Starting /usr/sbin/php-fpm83 -y "/services/config/php/php-fpm.conf" -F >>"/tmp/log/app.php_errors.log" 2>/dev/stderr &
netalertx-test-mount-config_ramdisk | Starting /usr/sbin/crond -c "/services/config/crond" -f -L "/tmp/log/crond.log" >>"/tmp/log/crond.log" 2>&1 &
netalertx-test-mount-config_ramdisk | Starting python3 -m server > /tmp/log/stdout.log 2> >(tee /tmp/log/stderr.log >&2)
netalertx-test-mount-config_ramdisk | Starting /usr/sbin/nginx -p "/tmp/run/" -c "/tmp/nginx-active-config/netalertx.conf" -g "error_log /dev/stderr; error_log /tmp/log/nginx-error.log; pid /tmp/run/nginx.pid; daemon off;" &
netalertx-test-mount-config_ramdisk | Successfully updated IEEE OUI database (111620 entries)
==========================================
@@ -993,12 +997,12 @@ netalertx-test-mount-config_unwritable | --> storage permission
netalertx-test-mount-config_unwritable | --> mounts.py
netalertx-test-mount-config_unwritable | Path | Writeable | Mount | RAMDisk | Performance | DataLoss
netalertx-test-mount-config_unwritable | ------------------------------------+-----------+-------+---------+-------------+----------
netalertx-test-mount-config_unwritable | --> storage permission
netalertx-test-mount-config_unwritable | --> mounts.py
netalertx-test-mount-config_unwritable | Path | Writeable | Mount | RAMDisk | Performance | DataLoss
netalertx-test-mount-config_unwritable | ------------------------------------+-----------+-------+---------+-------------+----------
netalertx-test-mount-config_unwritable | /app/db | ✅ | ✅ | | | ✅
netalertx-test-mount-config_unwritable | /app/config | | ✅ | | | ✅
netalertx-test-mount-config_unwritable | /data/db | ✅ | ✅ | | | ✅
netalertx-test-mount-config_unwritable | /data/config | ❌ | ✅ | | | ✅
netalertx-test-mount-config_unwritable | /tmp/api | ✅ | ✅ | ✅ | ✅ | ✅
netalertx-test-mount-config_unwritable | /tmp/log | ✅ | ✅ | ✅ | ✅ | ✅
netalertx-test-mount-config_unwritable | /tmp/run | ✅ | ✅ | | | ✅
netalertx-test-mount-config_unwritable | /tmp/nginx-active-config | | ✅ | | | ✅
netalertx-test-mount-config_unwritable | --> first run config
netalertx-test-mount-config_unwritable | --> first run db

View File

@@ -0,0 +1,57 @@
import os
import pathlib
import subprocess
import pytest
def _announce(request: pytest.FixtureRequest, message: str) -> None:
reporter = request.config.pluginmanager.get_plugin("terminalreporter")
if reporter: # pragma: no branch - depends on pytest runner
reporter.write_line(message)
else:
print(message)
@pytest.fixture(scope="session", autouse=True)
def build_netalertx_test_image(request: pytest.FixtureRequest) -> None:
"""Build the docker test image before running any docker-based tests."""
image = os.environ.get("NETALERTX_TEST_IMAGE", "netalertx-test")
project_root = pathlib.Path(__file__).resolve().parents[2]
cmd = [
"docker",
"buildx",
"build",
"--load",
"-t",
image,
".",
]
_announce(request, f"[docker-tests] Building test image '{image}' using docker buildx")
env = os.environ.copy()
env.setdefault("DOCKER_BUILDKIT", "1")
result = subprocess.run(
cmd,
cwd=project_root,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True,
check=False,
env=env,
)
if result.returncode != 0:
_announce(request, f"[docker-tests] docker buildx failed for '{image}'")
pytest.fail(
"Docker buildx failed before running docker tests.\n"
f"Command: {' '.join(cmd)}\n"
f"STDOUT:\n{result.stdout}\n"
f"STDERR:\n{result.stderr}"
)
_announce(request, f"[docker-tests] docker buildx completed for '{image}'")

View File

@@ -1,10 +1,10 @@
'''
"""
This set of tests requires netalertx-test image built. Ensure netalertx-test image is built prior
to starting these tests or they will fail. netalertx-test image is generally rebuilt using the
to starting these tests or they will fail. netalertx-test image is generally rebuilt using the
Build Unit Test Docker Image task. but can be created manually with the following command executed
in the workspace:
docker buildx build -t netalertx-test .
'''
"""
import os
import pathlib
@@ -14,22 +14,30 @@ import uuid
import re
import pytest
#TODO: test ALWAYS_FRESH_INSTALL
#TODO: test new named volume mount
IMAGE = os.environ.get("NETALERTX_TEST_IMAGE", "netalertx-test")
GRACE_SECONDS = float(os.environ.get("NETALERTX_TEST_GRACE", "2"))
DEFAULT_CAPS = ["NET_RAW", "NET_ADMIN", "NET_BIND_SERVICE"]
VOLUME_MAP = {
"app_db": "/app/db",
"app_config": "/app/config",
"app_log": "/app/log",
"app_api": "/app/api",
"nginx_conf": "/services/config/nginx/conf.active",
"services_run": "/services/run",
CONTAINER_TARGETS: dict[str, str] = {
"data": "/data",
"app_db": "/data/db",
"data_db": "/data/db",
"app_config": "/data/config",
"data_config": "/data/config",
"app_log": "/tmp/log",
"log": "/tmp/log",
"app_api": os.environ.get("NETALERTX_API", "/tmp/api"),
"api": os.environ.get("NETALERTX_API", "/tmp/api"),
"nginx_conf": "/tmp/nginx/active-config",
"nginx_active": "/tmp/nginx/active-config",
"services_run": "/tmp/run",
}
DATA_SUBDIR_KEYS = ("app_db", "app_config")
OPTIONAL_TMP_KEYS = ("app_log", "app_api", "nginx_conf", "services_run")
VOLUME_MAP = CONTAINER_TARGETS
pytestmark = [pytest.mark.docker, pytest.mark.feature_complete]
@@ -91,25 +99,53 @@ def _chown_path(host_path: pathlib.Path, uid: int, gid: int) -> None:
raise RuntimeError(f"Failed to chown {host_path} to {uid}:{gid}") from exc
def _setup_mount_tree(tmp_path: pathlib.Path, prefix: str, seed_config: bool = True, seed_db: bool = True) -> dict[str, pathlib.Path]:
def _setup_mount_tree(
tmp_path: pathlib.Path,
prefix: str,
seed_config: bool = True,
seed_db: bool = True,
) -> dict[str, pathlib.Path]:
label = _unique_label(prefix)
base = tmp_path / f"{label}_MOUNT_ROOT"
base.mkdir()
paths: dict[str, pathlib.Path] = {}
for key, target in VOLUME_MAP.items():
# Create unified /data mount root
data_root = base / f"{label}_DATA_INTENTIONAL_NETALERTX_TEST"
data_root.mkdir(parents=True, exist_ok=True)
data_root.chmod(0o777)
paths["data"] = data_root
# Create required data subdirectories and aliases
db_dir = data_root / "db"
db_dir.mkdir(exist_ok=True)
db_dir.chmod(0o777)
paths["app_db"] = db_dir
paths["data_db"] = db_dir
config_dir = data_root / "config"
config_dir.mkdir(exist_ok=True)
config_dir.chmod(0o777)
paths["app_config"] = config_dir
paths["data_config"] = config_dir
# Optional /tmp mounts that certain tests intentionally bind
for key in OPTIONAL_TMP_KEYS:
folder_name = f"{label}_{key.upper()}_INTENTIONAL_NETALERTX_TEST"
host_path = base / folder_name
host_path.mkdir(parents=True, exist_ok=True)
# Make the directory writable so the container (running as UID 20211)
# can create files on first run even if the host owner differs.
try:
host_path.chmod(0o777)
except PermissionError:
# If we can't chmod (uncommon in CI), tests that require strict
# ownership will still run their own chown/chmod operations.
pass
paths[key] = host_path
# Provide backwards-compatible aliases where helpful
if key == "app_log":
paths["log"] = host_path
elif key == "app_api":
paths["api"] = host_path
elif key == "nginx_conf":
paths["nginx_active"] = host_path
# Determine repo root from env or by walking up from this file
repo_root_env = os.environ.get("NETALERTX_REPO_ROOT")
@@ -119,9 +155,11 @@ def _setup_mount_tree(tmp_path: pathlib.Path, prefix: str, seed_config: bool = T
repo_root = None
cur = pathlib.Path(__file__).resolve()
for parent in cur.parents:
if (parent / "pyproject.toml").exists() or (parent / ".git").exists() or (
if any([
(parent / "pyproject.toml").exists(),
(parent / ".git").exists(),
(parent / "back").exists() and (parent / "db").exists()
):
]):
repo_root = parent
break
if repo_root is None:
@@ -131,7 +169,9 @@ def _setup_mount_tree(tmp_path: pathlib.Path, prefix: str, seed_config: bool = T
config_file = paths["app_config"] / "app.conf"
config_src = repo_root / "back" / "app.conf"
if not config_src.exists():
print(f"[WARN] Seed file not found: {config_src}. Set NETALERTX_REPO_ROOT or run from repo root. Skipping copy.")
print(
f"[WARN] Seed file not found: {config_src}. Set NETALERTX_REPO_ROOT or run from repo root. Skipping copy."
)
else:
shutil.copyfile(config_src, config_file)
config_file.chmod(0o600)
@@ -139,7 +179,9 @@ def _setup_mount_tree(tmp_path: pathlib.Path, prefix: str, seed_config: bool = T
db_file = paths["app_db"] / "app.db"
db_src = repo_root / "db" / "app.db"
if not db_src.exists():
print(f"[WARN] Seed file not found: {db_src}. Set NETALERTX_REPO_ROOT or run from repo root. Skipping copy.")
print(
f"[WARN] Seed file not found: {db_src}. Set NETALERTX_REPO_ROOT or run from repo root. Skipping copy."
)
else:
shutil.copyfile(db_src, db_file)
db_file.chmod(0o600)
@@ -155,24 +197,58 @@ def _setup_fixed_mount_tree(base: pathlib.Path) -> dict[str, pathlib.Path]:
base.mkdir(parents=True)
paths: dict[str, pathlib.Path] = {}
for key in VOLUME_MAP:
data_root = base / "DATA_NETALERTX_TEST"
data_root.mkdir(parents=True, exist_ok=True)
data_root.chmod(0o777)
paths["data"] = data_root
db_dir = data_root / "db"
db_dir.mkdir(exist_ok=True)
db_dir.chmod(0o777)
paths["app_db"] = db_dir
paths["data_db"] = db_dir
config_dir = data_root / "config"
config_dir.mkdir(exist_ok=True)
config_dir.chmod(0o777)
paths["app_config"] = config_dir
paths["data_config"] = config_dir
for key in OPTIONAL_TMP_KEYS:
host_path = base / f"{key.upper()}_NETALERTX_TEST"
host_path.mkdir(parents=True, exist_ok=True)
host_path.chmod(0o777)
paths[key] = host_path
if key == "app_log":
paths["log"] = host_path
elif key == "app_api":
paths["api"] = host_path
elif key == "nginx_conf":
paths["nginx_active"] = host_path
return paths
def _build_volume_args(
paths: dict[str, pathlib.Path],
) -> list[tuple[str, str, bool]]:
return _build_volume_args_for_keys(paths, {"data"})
def _build_volume_args_for_keys(
paths: dict[str, pathlib.Path],
keys: set[str],
read_only: set[str] | None = None,
skip: set[str] | None = None,
) -> list[tuple[str, str, bool]]:
bindings: list[tuple[str, str, bool]] = []
for key, target in VOLUME_MAP.items():
if skip and key in skip:
continue
bindings.append((str(paths[key]), target, key in read_only if read_only else False))
read_only = read_only or set()
for key in keys:
if key not in CONTAINER_TARGETS:
raise KeyError(f"Unknown mount key {key}")
target = CONTAINER_TARGETS[key]
if key not in paths:
raise KeyError(f"Missing host path for key {key}")
bindings.append((str(paths[key]), target, key in read_only))
return bindings
@@ -195,9 +271,10 @@ def _run_container(
extra_args: list[str] | None = None,
volume_specs: list[str] | None = None,
sleep_seconds: float = GRACE_SECONDS,
wait_for_exit: bool = False,
) -> subprocess.CompletedProcess[str]:
name = f"netalertx-test-{label}-{uuid.uuid4().hex[:8]}".lower()
# Clean up any existing container with this name
subprocess.run(
["docker", "rm", "-f", name],
@@ -205,7 +282,7 @@ def _run_container(
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
)
cmd: list[str] = ["docker", "run", "--rm", "--name", name]
if network_mode:
@@ -246,13 +323,16 @@ def _run_container(
mounts_ls += f" {target}"
mounts_ls += " || true; echo '--- END MOUNTS ---'; \n"
script = (
mounts_ls
+ "sh /entrypoint.sh & pid=$!; "
+ f"sleep {sleep_seconds}; "
+ "if kill -0 $pid >/dev/null 2>&1; then kill -TERM $pid >/dev/null 2>&1 || true; fi; "
+ "wait $pid; code=$?; if [ $code -eq 143 ]; then exit 0; fi; exit $code"
)
if wait_for_exit:
script = mounts_ls + "sh /entrypoint.sh"
else:
script = "".join([
mounts_ls,
"sh /entrypoint.sh & pid=$!; ",
f"sleep {sleep_seconds}; ",
"if kill -0 $pid >/dev/null 2>&1; then kill -TERM $pid >/dev/null 2>&1 || true; fi; ",
"wait $pid; code=$?; if [ $code -eq 143 ]; then exit 0; fi; exit $code"
])
cmd.extend(["--entrypoint", "/bin/sh", IMAGE, "-c", script])
# Print the full Docker command for debugging
@@ -266,10 +346,9 @@ def _run_container(
check=False,
)
# Combine and clean stdout and stderr
stdouterr = (
re.sub(r'\x1b\[[0-9;]*m', '', result.stdout or '') +
re.sub(r'\x1b\[[0-9;]*m', '', result.stderr or '')
)
stdouterr = re.sub(r"\x1b\[[0-9;]*m", "", result.stdout or "") + re.sub(
r"\x1b\[[0-9;]*m", "", result.stderr or ""
)
result.output = stdouterr
# Print container output for debugging in every test run.
print("\n--- CONTAINER OUTPUT START ---")
@@ -279,7 +358,6 @@ def _run_container(
return result
def _assert_contains(result, snippet: str, cmd: list[str] = None) -> None:
output = result.output + result.stderr
if snippet not in output:
@@ -293,6 +371,58 @@ def _assert_contains(result, snippet: str, cmd: list[str] = None) -> None:
)
def _extract_mount_rows(output: str) -> dict[str, list[str]]:
rows: dict[str, list[str]] = {}
in_table = False
for raw_line in (output or "").splitlines():
line = raw_line.rstrip()
if not in_table:
if line.startswith(" Path") and "Writeable" in line:
in_table = True
continue
if not line.strip():
break
if line.lstrip().startswith("Path"):
continue
if set(line.strip()) <= {"-", "+"}:
continue
parts = [part.strip() for part in line.split("|")]
if len(parts) < 6:
continue
path = parts[0].strip()
rows[path] = parts[1:6]
return rows
def _assert_mount_row(
result,
path: str,
*,
write: str | None = None,
mount: str | None = None,
ramdisk: str | None = None,
performance: str | None = None,
dataloss: str | None = None,
) -> None:
rows = _extract_mount_rows(result.output)
if path not in rows:
raise AssertionError(
f"Mount table row for {path} not found. Rows: {sorted(rows)}\nOutput:\n{result.output}"
)
columns = rows[path]
labels = ["Writeable", "Mount", "RAMDisk", "Performance", "DataLoss"]
expectations = [write, mount, ramdisk, performance, dataloss]
for idx, expected in enumerate(expectations):
if expected is None:
continue
actual = columns[idx]
if actual != expected:
raise AssertionError(
f"{path} {labels[idx]} expected {expected}, got {actual}.\n"
f"Rows: {rows}\nOutput:\n{result.output}"
)
def _setup_zero_perm_dir(paths: dict[str, pathlib.Path], key: str) -> None:
"""Set up a directory with files and zero permissions for testing."""
if key in ["app_db", "app_config"]:
@@ -301,11 +431,11 @@ def _setup_zero_perm_dir(paths: dict[str, pathlib.Path], key: str) -> None:
else:
# Create a dummy file for other directories
(paths[key] / "dummy.txt").write_text("dummy")
# Chmod all files in the directory to 000
for f in paths[key].iterdir():
f.chmod(0)
# Chmod the directory itself to 000
paths[key].chmod(0)
@@ -314,7 +444,7 @@ def _restore_zero_perm_dir(paths: dict[str, pathlib.Path], key: str) -> None:
"""Restore permissions after zero perm test."""
# Chmod directory back to 700
paths[key].chmod(0o700)
# Chmod files back to appropriate permissions
for f in paths[key].iterdir():
if f.name in ["app.db", "app.conf"]:
@@ -323,7 +453,6 @@ def _restore_zero_perm_dir(paths: dict[str, pathlib.Path], key: str) -> None:
f.chmod(0o644)
def test_missing_capabilities_triggers_warning(tmp_path: pathlib.Path) -> None:
"""Test missing required capabilities - simulates insufficient container privileges.
@@ -335,7 +464,7 @@ def test_missing_capabilities_triggers_warning(tmp_path: pathlib.Path) -> None:
Sample message: "exec /bin/sh: operation not permitted"
"""
paths = _setup_mount_tree(tmp_path, "missing_caps")
volumes = _build_volume_args(paths)
volumes = _build_volume_args_for_keys(paths, {"data"})
result = _run_container(
"missing-caps",
volumes,
@@ -356,7 +485,7 @@ def test_running_as_root_is_blocked(tmp_path: pathlib.Path) -> None:
Sample message: "🚨 CRITICAL SECURITY ALERT: NetAlertX is running as ROOT (UID 0)!"
"""
paths = _setup_mount_tree(tmp_path, "run_as_root")
volumes = _build_volume_args(paths)
volumes = _build_volume_args_for_keys(paths, {"data", "nginx_conf"})
result = _run_container(
"run-as-root",
volumes,
@@ -364,29 +493,9 @@ def test_running_as_root_is_blocked(tmp_path: pathlib.Path) -> None:
)
_assert_contains(result, "NetAlertX is running as ROOT", result.args)
_assert_contains(result, "Permissions fixed for read-write paths.", result.args)
assert result.returncode == 0 # container warns but continues running, then terminated by test framework
def test_running_as_uid_1000_warns(tmp_path: pathlib.Path) -> None:
# No output assertion, just returncode check
"""Test running as wrong user - simulates using arbitrary user instead of netalertx.
7. Running as Wrong User: Simulates running as arbitrary user (UID 1000) instead
of netalertx user. Permission errors due to incorrect user context.
Expected: Permission errors, guidance to use correct user.
Check script: /entrypoint.d/60-user-netalertx.sh
Sample message: "⚠️ ATTENTION: NetAlertX is running as UID 1000:1000. Hardened permissions..."
"""
paths = _setup_mount_tree(tmp_path, "run_as_1000")
volumes = _build_volume_args(paths)
result = _run_container(
"run-as-1000",
volumes,
user="1000:1000",
)
_assert_contains(result, "NetAlertX is running as UID 1000:1000", result.args)
assert (
result.returncode == 0
) # container warns but continues running, then terminated by test framework
def test_missing_host_network_warns(tmp_path: pathlib.Path) -> None:
@@ -403,7 +512,7 @@ def test_missing_host_network_warns(tmp_path: pathlib.Path) -> None:
base = tmp_path / "missing_host_net_base"
paths = _setup_fixed_mount_tree(base)
# Ensure directories are writable and owned by netalertx user so container can operate
for key in ["app_db", "app_config", "app_log", "app_api", "services_run", "nginx_conf"]:
for key in ["data", "app_db", "app_config"]:
paths[key].chmod(0o777)
_chown_netalertx(paths[key])
# Create a config file so the writable check passes
@@ -411,7 +520,7 @@ def test_missing_host_network_warns(tmp_path: pathlib.Path) -> None:
config_file.write_text("test config")
config_file.chmod(0o666)
_chown_netalertx(config_file)
volumes = _build_volume_args(paths)
volumes = _build_volume_args_for_keys(paths, {"data"})
result = _run_container(
"missing-host-network",
volumes,
@@ -420,89 +529,155 @@ def test_missing_host_network_warns(tmp_path: pathlib.Path) -> None:
_assert_contains(result, "not running with --network=host", result.args)
def test_missing_app_conf_triggers_seed(tmp_path: pathlib.Path) -> None:
"""Test missing configuration file seeding - simulates corrupted/missing app.conf.
9. Missing Configuration File: Simulates corrupted/missing app.conf.
Container automatically regenerates default configuration on startup.
Expected: Automatic regeneration of default configuration.
Check script: /entrypoint.d/15-first-run-config.sh
Sample message: "Default configuration written to"
"""
base = tmp_path / "missing_app_conf_base"
paths = _setup_fixed_mount_tree(base)
# Ensure directories are writable and owned by netalertx user so container can operate
for key in ["app_db", "app_config", "app_log", "app_api", "services_run", "nginx_conf"]:
paths[key].chmod(0o777)
_chown_netalertx(paths[key])
(paths["app_config"] / "testfile.txt").write_text("test")
volumes = _build_volume_args(paths)
result = _run_container("missing-app-conf", volumes, sleep_seconds=5)
_assert_contains(result, "Default configuration written to", result.args)
assert result.returncode == 0
# NOTE: The following runtime-behavior tests depended on the entrypoint continuing even when
# /data was mounted without write permissions. With fail-fast enabled we must supply a pre-owned
# (UID/GID 20211) data volume, which this dev container cannot provide for bind mounts. Once the
# docker tests switch to compose-managed fixtures, restore these cases by moving them back to the
# top level.
def test_missing_app_db_triggers_seed(tmp_path: pathlib.Path) -> None:
"""Test missing database file seeding - simulates corrupted/missing app.db.
if False: # pragma: no cover - placeholder until writable /data fixtures exist for these flows
def test_running_as_uid_1000_warns(tmp_path: pathlib.Path) -> None:
# No output assertion, just returncode check
"""Test running as wrong user - simulates using arbitrary user instead of netalertx.
10. Missing Database File: Simulates corrupted/missing app.db.
Container automatically creates initial database schema on startup.
Expected: Automatic creation of initial database schema.
7. Running as Wrong User: Simulates running as arbitrary user (UID 1000) instead
of netalertx user. Permission errors due to incorrect user context.
Expected: Permission errors, guidance to use correct user.
Check script: /entrypoint.d/20-first-run-db.sh
Sample message: "Building initial database schema"
"""
base = tmp_path / "missing_app_db_base"
paths = _setup_fixed_mount_tree(base)
_chown_netalertx(paths["app_db"])
(paths["app_db"] / "testfile.txt").write_text("test")
volumes = _build_volume_args(paths)
result = _run_container("missing-app-db", volumes, user="20211:20211", sleep_seconds=5)
_assert_contains(result, "Building initial database schema", result.args)
assert result.returncode != 0
def test_custom_port_without_writable_conf(tmp_path: pathlib.Path) -> None:
"""Test custom port configuration without writable nginx config mount.
4. Custom Port Without Nginx Config Mount: Simulates setting custom LISTEN_ADDR/PORT
without mounting nginx config. Container starts but uses default address.
Expected: Container starts but uses default address, warning about missing config mount.
Check script: check-nginx-config.sh
Sample messages: "⚠️ ATTENTION: Nginx configuration mount /services/config/nginx/conf.active is missing."
"⚠️ ATTENTION: Unable to write to /services/config/nginx/conf.active/netalertx.conf."
TODO: Custom ports can only be assigned when we have the PORT=something, and in that case
the /config.active partition shows up in the messages. It SHOULD exit if port is specified
and not writeable and I'm not sure it will.
RESOLVED: When PORT is specified but nginx config is not writable, the container warns
"Unable to write to /services/config/nginx/conf.active/netalertx.conf" but does NOT exit.
It continues with startup and fails later for other reasons if any directories are not writable.
"""
paths = _setup_mount_tree(tmp_path, "custom_port_ro_conf")
# Ensure other directories are writable so container gets to nginx config check
for key in ["app_db", "app_config", "app_log", "app_api", "services_run"]:
paths[key].chmod(0o777)
paths["nginx_conf"].chmod(0o500)
volumes = _build_volume_args(paths)
try:
Check script: /entrypoint.d/60-user-netalertx.sh
Sample message: "⚠️ ATTENTION: NetAlertX is running as UID 1000:1000. Hardened permissions..."
"""
paths = _setup_mount_tree(tmp_path, "run_as_1000")
volumes = _build_volume_args_for_keys(paths, {"data"})
result = _run_container(
"custom-port-ro-conf",
"run-as-1000",
volumes,
user="1000:1000",
)
_assert_contains(result, "NetAlertX is running as UID 1000:1000", result.args)
def test_missing_app_conf_triggers_seed(tmp_path: pathlib.Path) -> None:
"""Test missing configuration file seeding - simulates corrupted/missing app.conf.
9. Missing Configuration File: Simulates corrupted/missing app.conf.
Container automatically regenerates default configuration on startup.
Expected: Automatic regeneration of default configuration.
Check script: /entrypoint.d/15-first-run-config.sh
Sample message: "Default configuration written to"
"""
base = tmp_path / "missing_app_conf_base"
paths = _setup_fixed_mount_tree(base)
for key in ["data", "app_db", "app_config"]:
paths[key].chmod(0o777)
_chown_netalertx(paths[key])
(paths["app_config"] / "testfile.txt").write_text("test")
volumes = _build_volume_args_for_keys(paths, {"data"})
result = _run_container("missing-app-conf", volumes, sleep_seconds=5)
_assert_contains(result, "Default configuration written to", result.args)
assert result.returncode == 0
def test_missing_app_db_triggers_seed(tmp_path: pathlib.Path) -> None:
"""Test missing database file seeding - simulates corrupted/missing app.db.
10. Missing Database File: Simulates corrupted/missing app.db.
Container automatically creates initial database schema on startup.
Expected: Automatic creation of initial database schema.
Check script: /entrypoint.d/20-first-run-db.sh
Sample message: "Building initial database schema"
"""
base = tmp_path / "missing_app_db_base"
paths = _setup_fixed_mount_tree(base)
_chown_netalertx(paths["app_db"])
(paths["app_db"] / "testfile.txt").write_text("test")
volumes = _build_volume_args_for_keys(paths, {"data"})
result = _run_container(
"missing-app-db",
volumes,
env={"PORT": "24444", "LISTEN_ADDR": "127.0.0.1"},
user="20211:20211",
sleep_seconds=5,
wait_for_exit=True,
)
_assert_contains(result, "Unable to write to", result.args)
_assert_contains(result, "/services/config/nginx/conf.active/netalertx.conf", result.args)
# TODO: Should this exit when PORT is specified but nginx config is not writable?
# Currently it just warns and continues
_assert_contains(result, "Building initial database schema", result.args)
assert result.returncode != 0
finally:
paths["nginx_conf"].chmod(0o755)
def test_custom_port_without_writable_conf(tmp_path: pathlib.Path) -> None:
"""Test custom port configuration without writable nginx config mount.
4. Custom Port Without Nginx Config Mount: Simulates setting custom LISTEN_ADDR/PORT
without mounting nginx config. Container starts but uses default address.
Expected: Container starts but uses default address, warning about missing config mount.
Check script: check-nginx-config.sh
Sample messages: "⚠️ ATTENTION: Nginx configuration mount /tmp/nginx/active-config is missing."
"⚠️ ATTENTION: Unable to write to /tmp/nginx/active-config/netalertx.conf."
"""
paths = _setup_mount_tree(tmp_path, "custom_port_ro_conf")
for key in ["app_db", "app_config", "app_log", "app_api", "services_run"]:
paths[key].chmod(0o777)
paths["nginx_conf"].chmod(0o500)
volumes = _build_volume_args_for_keys(
paths,
{"data", "app_log", "app_api", "services_run", "nginx_conf"},
)
try:
result = _run_container(
"custom-port-ro-conf",
volumes,
env={"PORT": "24444", "LISTEN_ADDR": "127.0.0.1"},
user="20211:20211",
sleep_seconds=5,
)
_assert_contains(result, "Unable to write to", result.args)
_assert_contains(
result, f"{VOLUME_MAP['nginx_conf']}/netalertx.conf", result.args
)
assert result.returncode != 0
finally:
paths["nginx_conf"].chmod(0o755)
def test_excessive_capabilities_warning(tmp_path: pathlib.Path) -> None:
"""Test excessive capabilities detection - simulates container with extra capabilities.
11. Excessive Capabilities: Simulates container with capabilities beyond the required
NET_ADMIN, NET_RAW, and NET_BIND_SERVICE.
Expected: Warning about excessive capabilities detected.
Check script: 90-excessive-capabilities.sh
Sample message: "Excessive capabilities detected"
"""
paths = _setup_mount_tree(tmp_path, "excessive_caps")
volumes = _build_volume_args_for_keys(paths, {"data"})
result = _run_container(
"excessive-caps",
volumes,
extra_args=["--cap-add=SYS_ADMIN", "--cap-add=NET_BROADCAST"],
sleep_seconds=5,
)
_assert_contains(result, "Excessive capabilities detected", result.args)
_assert_contains(result, "bounding caps:", result.args)
def test_appliance_integrity_read_write_mode(tmp_path: pathlib.Path) -> None:
"""Test appliance integrity - simulates running with read-write root filesystem.
12. Appliance Integrity: Simulates running container with read-write root filesystem
instead of read-only mode.
Expected: Warning about running in read-write mode instead of read-only.
Check script: 95-appliance-integrity.sh
Sample message: "Container is running as read-write, not in read-only mode"
"""
paths = _setup_mount_tree(tmp_path, "appliance_integrity")
volumes = _build_volume_args_for_keys(paths, {"data"})
result = _run_container("appliance-integrity", volumes, sleep_seconds=5)
_assert_contains(
result, "Container is running as read-write, not in read-only mode", result.args
)
_assert_contains(result, "read-only: true", result.args)
def test_zero_permissions_app_db_dir(tmp_path: pathlib.Path) -> None:
"""Test zero permissions - simulates mounting directories/files with no permissions.
@@ -512,11 +687,16 @@ def test_zero_permissions_app_db_dir(tmp_path: pathlib.Path) -> None:
"""
paths = _setup_mount_tree(tmp_path, "chmod_app_db")
_setup_zero_perm_dir(paths, "app_db")
volumes = _build_volume_args(paths)
volumes = _build_volume_args_for_keys(paths, {"data"})
try:
result = _run_container("chmod-app-db", volumes, user="20211:20211")
result = _run_container(
"chmod-app-db",
volumes,
user="20211:20211",
wait_for_exit=True,
)
# Check that the mounts table shows the app_db directory as not writeable
_assert_contains(result, "/app/db | ❌ |", result.args)
_assert_mount_row(result, VOLUME_MAP["app_db"], write="")
# Check that configuration issues are detected
_assert_contains(result, "Configuration issues detected", result.args)
assert result.returncode != 0
@@ -533,11 +713,16 @@ def test_zero_permissions_app_config_dir(tmp_path: pathlib.Path) -> None:
"""
paths = _setup_mount_tree(tmp_path, "chmod_app_config")
_setup_zero_perm_dir(paths, "app_config")
volumes = _build_volume_args(paths)
volumes = _build_volume_args_for_keys(paths, {"data"})
try:
result = _run_container("chmod-app-config", volumes, user="20211:20211")
result = _run_container(
"chmod-app-config",
volumes,
user="20211:20211",
wait_for_exit=True,
)
# Check that the mounts table shows the app_config directory as not writeable
_assert_contains(result, "/app/config | ❌ |", result.args)
_assert_mount_row(result, VOLUME_MAP["app_config"], write="")
# Check that configuration issues are detected
_assert_contains(result, "Configuration issues detected", result.args)
assert result.returncode != 0
@@ -561,14 +746,23 @@ def test_mandatory_folders_creation(tmp_path: pathlib.Path) -> None:
plugins_log_dir = paths["app_log"] / "plugins"
if plugins_log_dir.exists():
shutil.rmtree(plugins_log_dir)
# Ensure other directories are writable and owned by netalertx user so container gets past mounts.py
for key in ["app_db", "app_config", "app_log", "app_api", "services_run", "nginx_conf"]:
for key in [
"app_db",
"app_config",
"app_log",
"app_api",
"services_run",
"nginx_conf",
]:
paths[key].chmod(0o777)
_chown_netalertx(paths[key]) # Ensure all directories are owned by netalertx
volumes = _build_volume_args(paths)
result = _run_container("mandatory-folders", volumes, user="20211:20211", sleep_seconds=5)
volumes = _build_volume_args_for_keys(paths, {"data"})
result = _run_container(
"mandatory-folders", volumes, user="20211:20211", sleep_seconds=5
)
_assert_contains(result, "Creating Plugins log", result.args)
# The container will fail at writable config due to permission issues, but we just want to verify
# that mandatory folders creation ran successfully
@@ -588,56 +782,24 @@ def test_writable_config_validation(tmp_path: pathlib.Path) -> None:
# Make config file read-only but keep directories writable so container gets past mounts.py
config_file = paths["app_config"] / "app.conf"
config_file.chmod(0o400) # Read-only for owner
# Ensure directories are writable and owned by netalertx user so container gets past mounts.py
for key in ["app_db", "app_config", "app_log", "app_api", "services_run", "nginx_conf"]:
for key in [
"app_db",
"app_config",
"app_log",
"app_api",
"services_run",
"nginx_conf",
]:
paths[key].chmod(0o777)
_chown_netalertx(paths[key])
volumes = _build_volume_args(paths)
result = _run_container("writable-config", volumes, user="20211:20211", sleep_seconds=5.0)
_assert_contains(result, "Read permission denied", result.args)
def test_excessive_capabilities_warning(tmp_path: pathlib.Path) -> None:
"""Test excessive capabilities detection - simulates container with extra capabilities.
11. Excessive Capabilities: Simulates container with capabilities beyond the required
NET_ADMIN, NET_RAW, and NET_BIND_SERVICE.
Expected: Warning about excessive capabilities detected.
Check script: 90-excessive-capabilities.sh
Sample message: "Excessive capabilities detected"
"""
paths = _setup_mount_tree(tmp_path, "excessive_caps")
volumes = _build_volume_args(paths)
# Add excessive capabilities beyond the required ones
volumes = _build_volume_args_for_keys(paths, {"data"})
result = _run_container(
"excessive-caps",
volumes,
extra_args=["--cap-add=SYS_ADMIN", "--cap-add=NET_BROADCAST"],
sleep_seconds=5,
"writable-config", volumes, user="20211:20211", sleep_seconds=5.0
)
_assert_contains(result, "Excessive capabilities detected", result.args)
_assert_contains(result, "bounding caps:", result.args)
# This warning doesn't cause failure by itself, but other issues might
def test_appliance_integrity_read_write_mode(tmp_path: pathlib.Path) -> None:
"""Test appliance integrity - simulates running with read-write root filesystem.
12. Appliance Integrity: Simulates running container with read-write root filesystem
instead of read-only mode.
Expected: Warning about running in read-write mode instead of read-only.
Check script: 95-appliance-integrity.sh
Sample message: "Container is running as read-write, not in read-only mode"
"""
paths = _setup_mount_tree(tmp_path, "appliance_integrity")
volumes = _build_volume_args(paths)
# Container runs read-write by default (not mounting root as read-only)
result = _run_container("appliance-integrity", volumes, sleep_seconds=5)
_assert_contains(result, "Container is running as read-write, not in read-only mode", result.args)
_assert_contains(result, "read-only: true", result.args)
# This warning doesn't cause failure by itself, but other issues might
_assert_contains(result, "Read permission denied", result.args)
def test_mount_analysis_ram_disk_performance(tmp_path: pathlib.Path) -> None:
@@ -653,17 +815,40 @@ def test_mount_analysis_ram_disk_performance(tmp_path: pathlib.Path) -> None:
paths = _setup_mount_tree(tmp_path, "ram_disk_mount")
# Mount persistent paths (db, config) on tmpfs to simulate RAM disk
volumes = [
(str(paths["app_log"]), "/app/log", False),
(str(paths["app_api"]), "/app/api", False),
(str(paths["services_run"]), "/services/run", False),
(str(paths["nginx_conf"]), "/services/config/nginx/conf.active", False),
(str(paths["app_log"]), VOLUME_MAP["app_log"], False),
(str(paths["app_api"]), VOLUME_MAP["app_api"], False),
(str(paths["services_run"]), VOLUME_MAP["services_run"], False),
(str(paths["nginx_conf"]), VOLUME_MAP["nginx_conf"], False),
]
# Use tmpfs mounts for persistent paths with proper permissions
extra_args = ["--tmpfs", "/app/db:uid=20211,gid=20211,mode=755", "--tmpfs", "/app/config:uid=20211,gid=20211,mode=755"]
result = _run_container("ram-disk-mount", volumes=volumes, extra_args=extra_args, user="20211:20211")
extra_args = [
"--tmpfs",
f"{VOLUME_MAP['app_db']}:uid=20211,gid=20211,mode=755",
"--tmpfs",
f"{VOLUME_MAP['app_config']}:uid=20211,gid=20211,mode=755",
]
result = _run_container(
"ram-disk-mount", volumes=volumes, extra_args=extra_args, user="20211:20211"
)
# Check that mounts table shows RAM disk detection for persistent paths
_assert_contains(result, "/app/db | ✅ | ✅ | ❌ | | ❌", result.args)
_assert_contains(result, "/app/config | ✅ | ✅ | ❌ | | ❌", result.args)
_assert_mount_row(
result,
VOLUME_MAP["app_db"],
write="",
mount="",
ramdisk="",
performance="",
dataloss="",
)
_assert_mount_row(
result,
VOLUME_MAP["app_config"],
write="",
mount="",
ramdisk="",
performance="",
dataloss="",
)
# Check that configuration issues are detected due to dataloss risk
_assert_contains(result, "Configuration issues detected", result.args)
assert result.returncode != 0
@@ -682,20 +867,40 @@ def test_mount_analysis_dataloss_risk(tmp_path: pathlib.Path) -> None:
paths = _setup_mount_tree(tmp_path, "dataloss_risk")
# Mount persistent paths (db, config) on tmpfs to simulate non-persistent storage
volumes = [
(str(paths["app_log"]), "/app/log", False),
(str(paths["app_api"]), "/app/api", False),
(str(paths["services_run"]), "/services/run", False),
(str(paths["nginx_conf"]), "/services/config/nginx/conf.active", False),
(str(paths["app_log"]), VOLUME_MAP["app_log"], False),
(str(paths["app_api"]), VOLUME_MAP["app_api"], False),
(str(paths["services_run"]), VOLUME_MAP["services_run"], False),
(str(paths["nginx_conf"]), VOLUME_MAP["nginx_conf"], False),
]
# Use tmpfs mounts for persistent paths with proper permissions
extra_args = ["--tmpfs", "/app/db:uid=20211,gid=20211,mode=755", "--tmpfs", "/app/config:uid=20211,gid=20211,mode=755"]
result = _run_container("dataloss-risk", volumes=volumes, extra_args=extra_args, user="20211:20211")
extra_args = [
"--tmpfs",
f"{VOLUME_MAP['app_db']}:uid=20211,gid=20211,mode=755",
"--tmpfs",
f"{VOLUME_MAP['app_config']}:uid=20211,gid=20211,mode=755",
]
result = _run_container(
"dataloss-risk", volumes=volumes, extra_args=extra_args, user="20211:20211"
)
# Check that mounts table shows dataloss risk for persistent paths on tmpfs
_assert_contains(result, "/app/db | ✅ | ✅ | ❌ | | ❌", result.args)
_assert_contains(result, "/app/config | ✅ | ✅ | ❌ | | ❌", result.args)
_assert_mount_row(
result,
VOLUME_MAP["app_db"],
write="",
mount="",
ramdisk="",
performance="",
dataloss="",
)
_assert_mount_row(
result,
VOLUME_MAP["app_config"],
write="",
mount="",
ramdisk="",
performance="",
dataloss="",
)
# Check that configuration issues are detected due to dataloss risk
_assert_contains(result, "Configuration issues detected", result.args)
assert result.returncode != 0

View File

@@ -17,10 +17,24 @@ import yaml
CONFIG_DIR = pathlib.Path(__file__).parent / "configurations"
ANSI_ESCAPE = re.compile(r"\x1B\[[0-9;]*[A-Za-z]")
CONTAINER_PATHS = {
"data": "/data",
"db": "/data/db",
"config": "/data/config",
"log": "/tmp/log",
"api": os.environ.get("NETALERTX_API", "/tmp/api"),
"run": "/tmp/run",
"nginx_active": "/tmp/nginx/active-config",
}
TMPFS_ROOT = "/tmp:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
pytestmark = [pytest.mark.docker, pytest.mark.compose]
IMAGE = os.environ.get("NETALERTX_TEST_IMAGE", "netalertx-test")
_CONFLICT_NAME_PATTERN = re.compile(r'The container name "/([^"]+)" is already in use')
# Docker Compose configurations for different test scenarios
COMPOSE_CONFIGS = {
"missing_capabilities": {
@@ -32,12 +46,11 @@ COMPOSE_CONFIGS = {
"cap_drop": ["ALL"], # Drop all capabilities
"tmpfs": ["/tmp:mode=777"],
"volumes": [
"./test_data/app_db:/app/db",
"./test_data/app_config:/app/config",
"./test_data/app_log:/app/log",
"./test_data/app_api:/app/api",
"./test_data/nginx_conf:/services/config/nginx/conf.active",
"./test_data/services_run:/services/run"
f"./test_data/data:{CONTAINER_PATHS['data']}",
f"./test_data/log:{CONTAINER_PATHS['log']}",
f"./test_data/api:{CONTAINER_PATHS['api']}",
f"./test_data/nginx_conf:{CONTAINER_PATHS['nginx_active']}",
f"./test_data/run:{CONTAINER_PATHS['run']}"
],
"environment": {
"TZ": "UTC"
@@ -54,12 +67,11 @@ COMPOSE_CONFIGS = {
"cap_add": ["NET_RAW", "NET_ADMIN", "NET_BIND_SERVICE"],
"tmpfs": ["/tmp:mode=777"],
"volumes": [
"./test_data/app_db:/app/db",
"./test_data/app_config:/app/config",
"./test_data/app_log:/app/log",
"./test_data/app_api:/app/api",
"./test_data/nginx_conf:/services/config/nginx/conf.active",
"./test_data/services_run:/services/run"
f"./test_data/data:{CONTAINER_PATHS['data']}",
f"./test_data/log:{CONTAINER_PATHS['log']}",
f"./test_data/api:{CONTAINER_PATHS['api']}",
f"./test_data/nginx_conf:{CONTAINER_PATHS['nginx_active']}",
f"./test_data/run:{CONTAINER_PATHS['run']}"
],
"environment": {
"TZ": "UTC"
@@ -77,24 +89,12 @@ COMPOSE_CONFIGS = {
"cap_drop": ["ALL"],
"cap_add": ["NET_RAW", "NET_ADMIN", "NET_BIND_SERVICE"],
"user": "20211:20211",
"tmpfs": [
"/app/log:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime",
"/app/api:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,sync,noatime,nodiratime",
"/services/config/nginx/conf.active:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime",
"/services/run:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime",
"/tmp:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime",
],
"tmpfs": [TMPFS_ROOT],
"volumes": [
{
"type": "volume",
"source": "__CONFIG_VOLUME__",
"target": "/app/config",
"read_only": False,
},
{
"type": "volume",
"source": "__DB_VOLUME__",
"target": "/app/db",
"source": "__DATA_VOLUME__",
"target": CONTAINER_PATHS["data"],
"read_only": False,
},
{
@@ -111,22 +111,31 @@ COMPOSE_CONFIGS = {
}
}
}
def _create_test_data_dirs(base_dir: pathlib.Path) -> None:
"""Create test data directories and files with write permissions for the container user."""
dirs = ["app_db", "app_config", "app_log", "app_api", "nginx_conf", "services_run"]
dirs = [
"data/db",
"data/config",
"log",
"api",
"nginx_conf",
"run",
]
for dir_name in dirs:
dir_path = base_dir / "test_data" / dir_name
dir_path.mkdir(parents=True, exist_ok=True)
dir_path.chmod(0o777)
# Create basic config file
config_file = base_dir / "test_data" / "app_config" / "app.conf"
config_file = base_dir / "test_data" / "data" / "config" / "app.conf"
if not config_file.exists():
config_file.write_text("# Test configuration\n")
config_file.chmod(0o666)
# Create basic db file
db_file = base_dir / "test_data" / "app_db" / "app.db"
db_file = base_dir / "test_data" / "data" / "db" / "app.db"
if not db_file.exists():
# Create a minimal SQLite database
import sqlite3
@@ -135,6 +144,13 @@ def _create_test_data_dirs(base_dir: pathlib.Path) -> None:
db_file.chmod(0o666)
def _extract_conflict_container_name(output: str) -> str | None:
match = _CONFLICT_NAME_PATTERN.search(output)
if match:
return match.group(1)
return None
def _run_docker_compose(
compose_file: pathlib.Path,
project_name: str,
@@ -163,18 +179,49 @@ def _run_docker_compose(
if env_vars:
env.update(env_vars)
try:
if detached:
up_result = subprocess.run(
up_cmd,
# Ensure no stale containers from previous runs; always clean before starting.
subprocess.run(
cmd + ["down", "-v"],
cwd=compose_file.parent,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True,
check=False,
env=env,
)
def _run_with_conflict_retry(run_cmd: list[str], run_timeout: int) -> subprocess.CompletedProcess:
retry_conflict = True
while True:
proc = subprocess.run(
run_cmd,
cwd=compose_file.parent,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True,
timeout=timeout,
timeout=run_timeout,
check=False,
env=env,
)
combined = (proc.stdout or "") + (proc.stderr or "")
if retry_conflict and "is already in use by container" in combined:
conflict_name = _extract_conflict_container_name(combined)
if conflict_name:
subprocess.run(
["docker", "rm", "-f", conflict_name],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True,
check=False,
env=env,
)
retry_conflict = False
continue
return proc
try:
if detached:
up_result = _run_with_conflict_retry(up_cmd, timeout)
logs_cmd = cmd + ["logs"]
logs_result = subprocess.run(
@@ -195,25 +242,16 @@ def _run_docker_compose(
stderr=(up_result.stderr or "") + (logs_result.stderr or ""),
)
else:
result = subprocess.run(
up_cmd,
cwd=compose_file.parent,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True,
timeout=timeout + 10,
check=False,
env=env,
)
result = _run_with_conflict_retry(up_cmd, timeout + 10)
except subprocess.TimeoutExpired:
# Clean up on timeout
subprocess.run(["docker", "compose", "-f", str(compose_file), "-p", project_name, "down", "-v"],
cwd=compose_file.parent, check=False, env=env)
cwd=compose_file.parent, check=False, env=env)
raise
# Always clean up
subprocess.run(["docker", "compose", "-f", str(compose_file), "-p", project_name, "down", "-v"],
cwd=compose_file.parent, check=False, env=env)
cwd=compose_file.parent, check=False, env=env)
# Combine stdout and stderr
result.output = result.stdout + result.stderr
@@ -256,8 +294,12 @@ def test_custom_port_with_unwritable_nginx_config_compose() -> None:
compose_file = CONFIG_DIR / "mount-tests" / "docker-compose.mount-test.active_config_unwritable.yml"
result = _run_docker_compose(compose_file, "netalertx-custom-port", env_vars={"PORT": "24444"})
# Keep verbose output for human debugging. Future automation must not remove this print; use
# the failedTest tool to trim context instead of stripping logs.
print("\n[compose output]", result.output)
# Check for nginx config write failure warning
assert "Unable to write to /services/config/nginx/conf.active/netalertx.conf" in result.output
assert f"Unable to write to {CONTAINER_PATHS['nginx_active']}/netalertx.conf" in result.output
# Container should still attempt to start but may fail for other reasons
# The key is that the nginx config write warning appears
@@ -304,11 +346,9 @@ def test_normal_startup_no_warnings_compose(tmp_path: pathlib.Path) -> None:
compose_config = copy.deepcopy(COMPOSE_CONFIGS["normal_startup"])
service = compose_config["services"]["netalertx"]
config_volume_name = f"{project_name}_config"
db_volume_name = f"{project_name}_db"
data_volume_name = f"{project_name}_data"
service["volumes"][0]["source"] = config_volume_name
service["volumes"][1]["source"] = db_volume_name
service["volumes"][0]["source"] = data_volume_name
service.setdefault("environment", {})
service["environment"].update({
@@ -317,8 +357,7 @@ def test_normal_startup_no_warnings_compose(tmp_path: pathlib.Path) -> None:
})
compose_config["volumes"] = {
config_volume_name: {},
db_volume_name: {},
data_volume_name: {},
}
compose_file = base_dir / "docker-compose.yml"
@@ -333,7 +372,25 @@ def test_normal_startup_no_warnings_compose(tmp_path: pathlib.Path) -> None:
# Check that startup completed without critical issues and mounts table shows success
assert "Startup pre-checks" in clean_output
assert "" not in clean_output
assert "/app/db | ✅" in clean_output
data_line = ""
data_parts: list[str] = []
for line in clean_output.splitlines():
if CONTAINER_PATHS['data'] not in line or '|' not in line:
continue
parts = [segment.strip() for segment in line.split('|')]
if len(parts) < 2:
continue
if parts[1] == CONTAINER_PATHS['data']:
data_line = line
data_parts = parts
break
assert data_line, "Expected /data row in mounts table"
parts = data_parts
assert parts[1] == CONTAINER_PATHS['data'], f"Unexpected path column in /data row: {parts}"
assert parts[2] == "" and parts[3] == "", f"Unexpected mount row values for /data: {parts[2:4]}"
# Ensure no critical errors or permission problems surfaced
assert "Write permission denied" not in clean_output
@@ -364,14 +421,13 @@ def test_ram_disk_mount_analysis_compose(tmp_path: pathlib.Path) -> None:
"user": "20211:20211",
"tmpfs": [
"/tmp:mode=777",
"/app/db", # RAM disk for persistent DB
"/app/config" # RAM disk for persistent config
CONTAINER_PATHS["data"], # RAM disk for persistent data root
],
"volumes": [
f"./test_data/app_log:/app/log",
f"./test_data/app_api:/app/api",
f"./test_data/nginx_conf:/services/config/nginx/conf.active",
f"./test_data/services_run:/services/run"
f"./test_data/log:{CONTAINER_PATHS['log']}",
f"./test_data/api:{CONTAINER_PATHS['api']}",
f"./test_data/nginx_conf:{CONTAINER_PATHS['nginx_active']}",
f"./test_data/run:{CONTAINER_PATHS['run']}"
],
"environment": {
"TZ": "UTC"
@@ -389,8 +445,7 @@ def test_ram_disk_mount_analysis_compose(tmp_path: pathlib.Path) -> None:
# Check that mounts table shows RAM disk detection and dataloss warnings
assert "Configuration issues detected" in result.output
assert "/app/db" in result.output
assert "/app/config" in result.output
assert CONTAINER_PATHS["data"] in result.output
assert result.returncode != 0 # Should fail due to dataloss risk
@@ -417,14 +472,13 @@ def test_dataloss_risk_mount_analysis_compose(tmp_path: pathlib.Path) -> None:
"user": "20211:20211",
"tmpfs": [
"/tmp:mode=777",
"/app/db:uid=20211,gid=20211", # Non-persistent for DB
"/app/config:uid=20211,gid=20211" # Non-persistent for config
f"{CONTAINER_PATHS['data']}:uid=20211,gid=20211", # Non-persistent for unified data
],
"volumes": [
f"./test_data/app_log:/app/log",
f"./test_data/app_api:/app/api",
f"./test_data/nginx_conf:/services/config/nginx/conf.active",
f"./test_data/services_run:/services/run"
f"./test_data/log:{CONTAINER_PATHS['log']}",
f"./test_data/api:{CONTAINER_PATHS['api']}",
f"./test_data/nginx_conf:{CONTAINER_PATHS['nginx_active']}",
f"./test_data/run:{CONTAINER_PATHS['run']}"
],
"environment": {
"TZ": "UTC"
@@ -442,6 +496,5 @@ def test_dataloss_risk_mount_analysis_compose(tmp_path: pathlib.Path) -> None:
# Check that mounts table shows dataloss risk detection
assert "Configuration issues detected" in result.output
assert "/app/db" in result.output
assert "/app/config" in result.output
assert result.returncode != 0 # Should fail due to dataloss risk
assert CONTAINER_PATHS["data"] in result.output
assert result.returncode != 0 # Should fail due to dataloss risk

View File

@@ -79,4 +79,4 @@ def test_no_app_conf_override_when_no_graphql_port():
# The script should exit successfully.
result = _run_entrypoint(env={"SKIP_TESTS": "1"}, check_only=True)
assert 'Setting APP_CONF_OVERRIDE to' not in result.stdout
assert result.returncode == 0
assert result.returncode == 0

View File

@@ -7,36 +7,45 @@ Uses pytest framework for proper test discovery and execution.
All tests use the mounts table. For reference, the mounts table looks like this:
Path | Writeable | Mount | RAMDisk | Performance | DataLoss
------------------------------------+-----------+-------+---------+-------------+----------
/app/db | ✅ | ❌ | | | ❌
/app/config | ✅ | ❌ | | | ❌
/app/api | ✅ | ❌ | ❌ | ❌ | ✅
/app/log | ✅ | ❌ | ❌ | ❌ | ✅
/services/run | ✅ | ❌ | ❌ | ❌ | ✅
/services/config/nginx/conf.active | ✅ | ❌ | ❌ | ❌ | ✅
Path | Writeable | Mount | RAMDisk | Performance | DataLoss
-------------------------+-----------+-------+---------+-------------+---------
/data/db | ✅ | ❌ | | | ❌
/data/config | ✅ | ❌ | | | ❌
/tmp/api | ✅ | ❌ | ❌ | ❌ | ✅
/tmp/log | ✅ | ❌ | ❌ | ❌ | ✅
/tmp/run | ✅ | ❌ | ❌ | ❌ | ✅
/tmp/nginx/active-config| ✅ | ❌ | ❌ | ❌ | ✅
Table Assertions:
- Use assert_table_row(output, path, writeable=True/False/None, mount=True/False/None, ...)
- Emojis are converted: ✅=True, ❌=False, =None
- Example: assert_table_row(output, "/app/db", writeable=True, mount=False, dataloss=False)
- Example: assert_table_row(output, "/data/db", writeable=True, mount=False, dataloss=False)
"""
import os
import subprocess
import pytest
import re
from pathlib import Path
from dataclasses import dataclass
from typing import List, Optional, Tuple, Union
from typing import List, Optional
# Test configurations directory
CONFIG_DIR = Path(__file__).parent / "configurations"
CONTAINER_PATHS = {
"db": "/data/db",
"config": "/data/config",
"api": os.environ.get("NETALERTX_API", "/tmp/api"),
"log": "/tmp/log",
"run": "/tmp/run",
"active_config": "/tmp/nginx/active-config",
}
@dataclass
class MountTableRow:
"""Represents a parsed row from the mount diagnostic table."""
path: str
writeable: bool
mount: bool
@@ -44,49 +53,66 @@ class MountTableRow:
performance: Optional[bool] # None for
dataloss: bool
class _Unset:
"""Sentinel object for optional expectations."""
UNSET = _Unset()
Expectation = Optional[bool] | _Unset
def _expected_to_emoji(value: Optional[bool]) -> str:
if value is True:
return ""
if value is False:
return ""
return ""
def parse_mount_table(output: str) -> List[MountTableRow]:
"""Parse the mount diagnostic table from stdout."""
rows = []
# Find the table in the output
lines = output.split('\n')
lines = output.split("\n")
table_start = None
for i, line in enumerate(lines):
if line.startswith(' Path ') and '|' in line:
if line.startswith(" Path ") and "|" in line:
table_start = i
break
if table_start is None:
return rows
# Skip header and separator lines
data_lines = lines[table_start + 2:]
data_lines = lines[table_start + 2 :]
for line in data_lines:
if '|' not in line or line.strip() == '':
if "|" not in line or line.strip() == "":
continue
# Split by | and clean up
parts = [part.strip() for part in line.split('|')]
parts = [part.strip() for part in line.split("|")]
if len(parts) < 6:
continue
path = parts[0]
if not path:
continue
# Convert emojis to boolean/none
def emoji_to_bool(emoji: str) -> Optional[bool]:
emoji = emoji.strip()
if emoji == '':
if emoji == "":
return True
elif emoji == '':
elif emoji == "":
return False
elif emoji == '':
elif emoji == "":
return None
return None
try:
row = MountTableRow(
path=path,
@@ -94,54 +120,74 @@ def parse_mount_table(output: str) -> List[MountTableRow]:
mount=emoji_to_bool(parts[2]),
ramdisk=emoji_to_bool(parts[3]),
performance=emoji_to_bool(parts[4]),
dataloss=emoji_to_bool(parts[5])
dataloss=emoji_to_bool(parts[5]),
)
rows.append(row)
except (IndexError, ValueError):
continue
return rows
def assert_table_row(output: str, expected_path: str,
writeable: Optional[bool] = None,
mount: Optional[bool] = None,
ramdisk: Optional[bool] = None,
performance: Optional[bool] = None,
dataloss: Optional[bool] = None) -> MountTableRow:
def assert_table_row(
output: str,
expected_path: str,
writeable: Expectation = UNSET,
mount: Expectation = UNSET,
ramdisk: Expectation = UNSET,
performance: Expectation = UNSET,
dataloss: Expectation = UNSET,
) -> MountTableRow:
"""Assert that a specific table row matches expected values."""
rows = parse_mount_table(output)
# Find the row for the expected path
matching_row = None
for row in rows:
if row.path == expected_path:
matching_row = row
break
assert matching_row is not None, f"Path '{expected_path}' not found in table. Available paths: {[r.path for r in rows]}"
# Check each field if specified
if writeable is not None:
assert matching_row.writeable == writeable, f"Path '{expected_path}': expected writeable={writeable}, got {matching_row.writeable}"
if mount is not None:
assert matching_row.mount == mount, f"Path '{expected_path}': expected mount={mount}, got {matching_row.mount}"
if ramdisk is not None:
assert matching_row.ramdisk == ramdisk, f"Path '{expected_path}': expected ramdisk={ramdisk}, got {matching_row.ramdisk}"
if performance is not None:
assert matching_row.performance == performance, f"Path '{expected_path}': expected performance={performance}, got {matching_row.performance}"
if dataloss is not None:
assert matching_row.dataloss == dataloss, f"Path '{expected_path}': expected dataloss={dataloss}, got {matching_row.dataloss}"
assert matching_row is not None, (
f"Path '{expected_path}' not found in table. Available paths: {[r.path for r in rows]}"
)
raw_line = None
for line in output.splitlines():
if line.strip().startswith(expected_path):
raw_line = line
break
assert raw_line is not None, f"Raw table line for '{expected_path}' not found in output."
raw_parts = [part.strip() for part in raw_line.split("|")]
assert len(raw_parts) >= 6, f"Malformed table row for '{expected_path}': {raw_line}"
def _check(field_name: str, expected: Expectation, actual: Optional[bool], column_index: int) -> None:
if expected is UNSET:
return
assert actual == expected, (
f"Path '{expected_path}': expected {field_name}={expected}, got {actual}"
)
expected_emoji = _expected_to_emoji(expected)
assert raw_parts[column_index] == expected_emoji, (
f"Path '{expected_path}': expected emoji {expected_emoji} for {field_name}, "
f"got '{raw_parts[column_index]}' in row: {raw_line}"
)
_check("writeable", writeable, matching_row.writeable, 1)
_check("mount", mount, matching_row.mount, 2)
_check("ramdisk", ramdisk, matching_row.ramdisk, 3)
_check("performance", performance, matching_row.performance, 4)
_check("dataloss", dataloss, matching_row.dataloss, 5)
return matching_row
@dataclass
class TestScenario:
"""Represents a test scenario for a specific path configuration."""
__test__ = False # Prevent pytest from collecting this as a test class
name: str
path_var: str
@@ -151,6 +197,7 @@ class TestScenario:
expected_issues: List[str] # List of expected issue types
expected_exit_code: int # Expected container exit code
@pytest.fixture(scope="session")
def netalertx_test_image():
"""Ensure the netalertx-test image exists."""
@@ -158,9 +205,7 @@ def netalertx_test_image():
# Check if image exists
result = subprocess.run(
["docker", "images", "-q", image_name],
capture_output=True,
text=True
["docker", "images", "-q", image_name], capture_output=True, text=True
)
if not result.stdout.strip():
@@ -168,11 +213,13 @@ def netalertx_test_image():
return image_name
@pytest.fixture
def test_scenario(request):
"""Fixture that provides test scenarios."""
return request.param
def create_test_scenarios() -> List[TestScenario]:
"""Create all test scenarios."""
@@ -180,19 +227,27 @@ def create_test_scenarios() -> List[TestScenario]:
# Define paths to test
paths = [
("db", "/app/db", True, "NETALERTX_DB"),
("config", "/app/config", True, "NETALERTX_CONFIG"),
("api", "/app/api", False, "NETALERTX_API"),
("log", "/app/log", False, "NETALERTX_LOG"),
("run", "/services/run", False, "SYSTEM_SERVICES_RUN"),
("active_config", "/services/config/nginx/conf.active", False, "SYSTEM_SERVICES_ACTIVE_CONFIG"),
("db", CONTAINER_PATHS["db"], True, "NETALERTX_DB"),
("config", CONTAINER_PATHS["config"], True, "NETALERTX_CONFIG"),
("api", CONTAINER_PATHS["api"], False, "NETALERTX_API"),
("log", CONTAINER_PATHS["log"], False, "NETALERTX_LOG"),
("run", CONTAINER_PATHS["run"], False, "SYSTEM_SERVICES_RUN"),
(
"active_config",
CONTAINER_PATHS["active_config"],
False,
"SYSTEM_SERVICES_ACTIVE_CONFIG",
),
]
# Test scenarios for each path
test_scenarios = [
("no-mount", ["table_issues", "warning_message"]), # Always issues
("ramdisk", []), # Good for non-persistent, bad for persistent
("mounted", ["table_issues", "warning_message"]), # Bad for non-persistent, good for persistent
(
"mounted",
["table_issues", "warning_message"],
), # Bad for non-persistent, good for persistent
("unwritable", ["table_issues", "warning_message"]), # Always issues
]
@@ -210,24 +265,69 @@ def create_test_scenarios() -> List[TestScenario]:
elif path_name == "active_config" and scenario_name == "unwritable":
# active_config unwritable: RAM disk issues detected
expected_issues = ["table_issues", "warning_message"]
elif path_name == "active_config" and scenario_name == "no-mount":
# Active config now lives on the internal tmpfs by default; missing host mount is healthy
expected_issues = []
compose_file = f"docker-compose.mount-test.{path_name}_{scenario_name}.yml"
# Determine expected exit code
expected_exit_code = 1 if scenario_name == "unwritable" else 0
scenarios.append(TestScenario(
name=f"{path_name}_{scenario_name}",
path_var=env_var,
container_path=container_path,
is_persistent=is_persistent,
docker_compose=compose_file,
expected_issues=expected_issues,
expected_exit_code=expected_exit_code
))
scenarios.append(
TestScenario(
name=f"{path_name}_{scenario_name}",
path_var=env_var,
container_path=container_path,
is_persistent=is_persistent,
docker_compose=compose_file,
expected_issues=expected_issues,
expected_exit_code=expected_exit_code,
)
)
return scenarios
def _print_compose_logs(
compose_file: Path,
project_name: str,
reason: str,
env: dict[str, str] | None = None,
) -> None:
"""Dump docker compose logs for debugging when a test fails."""
env = env or os.environ.copy()
cmd = [
"docker",
"compose",
"-f",
str(compose_file),
"-p",
project_name,
"logs",
"--no-color",
]
result = subprocess.run(
cmd,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True,
check=False,
env=env,
)
print("\n=== docker compose logs (DO NOT REMOVE) ===")
print(f"Reason: {reason}")
print("Command:", " ".join(cmd))
print(
"Note: If this output feels too large for your context window, redirect it to a file and read it back instead of deleting it."
)
print(result.stdout or "<no stdout>")
if result.stderr:
print("--- logs stderr ---")
print(result.stderr)
print("=== end docker compose logs ===\n")
def validate_scenario_table_output(output: str, test_scenario: TestScenario) -> None:
"""Validate the diagnostic table for scenarios that should report issues."""
@@ -235,75 +335,103 @@ def validate_scenario_table_output(output: str, test_scenario: TestScenario) ->
return
try:
if test_scenario.name.startswith('db_'):
if test_scenario.name == 'db_ramdisk':
# db on ramdisk: mount=True, ramdisk=False (detected), dataloss=False (risk)
assert_table_row(output, '/app/db', mount=True, ramdisk=False, dataloss=False)
elif test_scenario.name == 'db_no-mount':
# db not mounted: mount=False, dataloss=False (risk)
assert_table_row(output, '/app/db', mount=False, dataloss=False)
elif test_scenario.name == 'db_unwritable':
# db read-only: writeable=False
assert_table_row(output, '/app/db', writeable=False)
if test_scenario.name.startswith("db_"):
if test_scenario.name == "db_ramdisk":
assert_table_row(
output,
CONTAINER_PATHS["db"],
mount=True,
ramdisk=False,
dataloss=False,
)
elif test_scenario.name == "db_no-mount":
assert_table_row(
output, CONTAINER_PATHS["db"], mount=False, dataloss=False
)
elif test_scenario.name == "db_unwritable":
assert_table_row(output, CONTAINER_PATHS["db"], writeable=False)
elif test_scenario.name.startswith('config_'):
if test_scenario.name == 'config_ramdisk':
# config on ramdisk: mount=True, ramdisk=False (detected), dataloss=False (risk)
assert_table_row(output, '/app/config', mount=True, ramdisk=False, dataloss=False)
elif test_scenario.name == 'config_no-mount':
# config not mounted: mount=False, dataloss=False (risk)
assert_table_row(output, '/app/config', mount=False, dataloss=False)
elif test_scenario.name == 'config_unwritable':
# config read-only: writeable=False
assert_table_row(output, '/app/config', writeable=False)
elif test_scenario.name.startswith("config_"):
if test_scenario.name == "config_ramdisk":
assert_table_row(
output,
CONTAINER_PATHS["config"],
mount=True,
ramdisk=False,
dataloss=False,
)
elif test_scenario.name == "config_no-mount":
assert_table_row(
output, CONTAINER_PATHS["config"], mount=False, dataloss=False
)
elif test_scenario.name == "config_unwritable":
assert_table_row(output, CONTAINER_PATHS["config"], writeable=False)
elif test_scenario.name.startswith('api_'):
if test_scenario.name == 'api_mounted':
# api with volume mount: mount=True, performance=False (not ramdisk)
assert_table_row(output, '/app/api', mount=True, performance=False)
elif test_scenario.name == 'api_no-mount':
# api not mounted: mount=False, performance=False (not ramdisk)
assert_table_row(output, '/app/api', mount=False, performance=False)
elif test_scenario.name == 'api_unwritable':
# api read-only: writeable=False
assert_table_row(output, '/app/api', writeable=False)
elif test_scenario.name.startswith("api_"):
if test_scenario.name == "api_mounted":
assert_table_row(
output, CONTAINER_PATHS["api"], mount=True, performance=False
)
elif test_scenario.name == "api_no-mount":
assert_table_row(
output, CONTAINER_PATHS["api"], mount=False, performance=False
)
elif test_scenario.name == "api_unwritable":
assert_table_row(output, CONTAINER_PATHS["api"], writeable=False)
elif test_scenario.name.startswith('log_'):
if test_scenario.name == 'log_mounted':
# log with volume mount: mount=True, performance=False (not ramdisk)
assert_table_row(output, '/app/log', mount=True, performance=False)
elif test_scenario.name == 'log_no-mount':
# log not mounted: mount=False, performance=False (not ramdisk)
assert_table_row(output, '/app/log', mount=False, performance=False)
elif test_scenario.name == 'log_unwritable':
# log read-only: writeable=False
assert_table_row(output, '/app/log', writeable=False)
elif test_scenario.name.startswith("log_"):
if test_scenario.name == "log_mounted":
assert_table_row(
output, CONTAINER_PATHS["log"], mount=True, performance=False
)
elif test_scenario.name == "log_no-mount":
assert_table_row(
output, CONTAINER_PATHS["log"], mount=False, performance=False
)
elif test_scenario.name == "log_unwritable":
assert_table_row(output, CONTAINER_PATHS["log"], writeable=False)
elif test_scenario.name.startswith('run_'):
if test_scenario.name == 'run_mounted':
# run with volume mount: mount=True, performance=False (not ramdisk)
assert_table_row(output, '/services/run', mount=True, performance=False)
elif test_scenario.name == 'run_no-mount':
# run not mounted: mount=False, performance=False (not ramdisk)
assert_table_row(output, '/services/run', mount=False, performance=False)
elif test_scenario.name == 'run_unwritable':
# run read-only: writeable=False
assert_table_row(output, '/services/run', writeable=False)
elif test_scenario.name.startswith("run_"):
if test_scenario.name == "run_mounted":
assert_table_row(
output, CONTAINER_PATHS["run"], mount=True, performance=False
)
elif test_scenario.name == "run_no-mount":
assert_table_row(
output, CONTAINER_PATHS["run"], mount=False, performance=False
)
elif test_scenario.name == "run_unwritable":
assert_table_row(output, CONTAINER_PATHS["run"], writeable=False)
elif test_scenario.name.startswith('active_config_'):
if test_scenario.name == 'active_config_mounted':
# active_config with volume mount: mount=True, performance=False (not ramdisk)
assert_table_row(output, '/services/config/nginx/conf.active', mount=True, performance=False)
elif test_scenario.name == 'active_config_no-mount':
# active_config not mounted: mount=False, performance=False (not ramdisk)
assert_table_row(output, '/services/config/nginx/conf.active', mount=False, performance=False)
elif test_scenario.name == 'active_config_unwritable':
# active_config unwritable: RAM disk issues detected
assert_table_row(output, '/services/config/nginx/conf.active', ramdisk=False, performance=False)
elif test_scenario.name.startswith("active_config_"):
if test_scenario.name == "active_config_mounted":
assert_table_row(
output,
CONTAINER_PATHS["active_config"],
mount=True,
performance=False,
)
elif test_scenario.name == "active_config_no-mount":
assert_table_row(
output,
CONTAINER_PATHS["active_config"],
mount=True,
ramdisk=True,
performance=True,
dataloss=True,
)
elif test_scenario.name == "active_config_unwritable":
assert_table_row(
output,
CONTAINER_PATHS["active_config"],
ramdisk=False,
performance=False,
)
except AssertionError as e:
pytest.fail(f"Table validation failed for {test_scenario.name}: {e}")
@pytest.mark.parametrize("test_scenario", create_test_scenarios(), ids=lambda s: s.name)
@pytest.mark.docker
def test_mount_diagnostic(netalertx_test_image, test_scenario):
@@ -315,83 +443,111 @@ def test_mount_diagnostic(netalertx_test_image, test_scenario):
# Start container
project_name = f"mount-test-{test_scenario.name.replace('_', '-')}"
# Remove any existing containers with the same project name
cmd_down = [
"docker-compose", "-f", str(compose_file),
"-p", project_name, "down", "-v"
]
subprocess.run(cmd_down, capture_output=True, timeout=30)
cmd_up = [
"docker-compose", "-f", str(compose_file),
"-p", project_name, "up", "-d"
compose_env = os.environ.copy()
base_cmd = [
"docker",
"compose",
"-f",
str(compose_file),
"-p",
project_name,
]
logs_emitted = False
result_up = subprocess.run(cmd_up, capture_output=True, text=True, timeout=60)
if result_up.returncode != 0:
pytest.fail(
f"Failed to start container: {result_up.stderr}\n"
f"STDOUT: {result_up.stdout}"
)
def ensure_logs(reason: str) -> None:
nonlocal logs_emitted
if logs_emitted:
return
_print_compose_logs(compose_file, project_name, reason, env=compose_env)
logs_emitted = True
# Remove any existing containers with the same project name
subprocess.run(
base_cmd + ["down", "-v"], capture_output=True, timeout=30, env=compose_env
)
cmd_up = base_cmd + ["up", "-d"]
try:
result_up = subprocess.run(
cmd_up, capture_output=True, text=True, timeout=20, env=compose_env
)
if result_up.returncode != 0:
ensure_logs("compose up failed")
pytest.fail(
f"Failed to start container: {result_up.stderr}\n"
f"STDOUT: {result_up.stdout}"
)
# Wait for container to be ready
import time
time.sleep(4)
time.sleep(1)
# Check if container is still running
container_name = f"netalertx-test-mount-{test_scenario.name}"
result_ps = subprocess.run(
["docker", "ps", "-q", "-f", f"name={container_name}"],
capture_output=True, text=True
capture_output=True,
text=True,
)
if not result_ps.stdout.strip():
# Container exited - check the exit code
result_inspect = subprocess.run(
["docker", "inspect", container_name, "--format={{.State.ExitCode}}"],
capture_output=True, text=True
capture_output=True,
text=True,
)
actual_exit_code = int(result_inspect.stdout.strip())
# Assert the exit code matches expected
assert actual_exit_code == test_scenario.expected_exit_code, (
f"Container {container_name} exited with code {actual_exit_code}, "
f"expected {test_scenario.expected_exit_code}"
)
if actual_exit_code != test_scenario.expected_exit_code:
ensure_logs("unexpected exit code")
pytest.fail(
f"Container {container_name} exited with code {actual_exit_code}, "
f"expected {test_scenario.expected_exit_code}"
)
# Check the logs to see if it detected the expected issues
result_logs = subprocess.run(
["docker", "logs", container_name],
capture_output=True, text=True
["docker", "logs", container_name], capture_output=True, text=True
)
logs = result_logs.stdout + result_logs.stderr
if test_scenario.expected_issues:
validate_scenario_table_output(logs, test_scenario)
return # Test passed - container correctly detected issues and exited
# Container is still running - run diagnostic tool
cmd_exec = [
"docker", "exec", "--user", "netalertx", container_name,
"python3", "/entrypoint.d/10-mounts.py"
"docker",
"exec",
"--user",
"netalertx",
container_name,
"python3",
"/entrypoint.d/10-mounts.py",
]
result_exec = subprocess.run(cmd_exec, capture_output=True, text=True, timeout=30)
result_exec = subprocess.run(
cmd_exec, capture_output=True, text=True, timeout=30
)
diagnostic_output = result_exec.stdout + result_exec.stderr
# The diagnostic tool returns 1 for unwritable paths except active_config, which only warns
if test_scenario.name.startswith('active_config_') and 'unwritable' in test_scenario.name:
if (test_scenario.name.startswith("active_config_") and "unwritable" in test_scenario.name):
expected_tool_exit = 0
elif 'unwritable' in test_scenario.name:
elif "unwritable" in test_scenario.name:
expected_tool_exit = 1
else:
expected_tool_exit = 0
assert result_exec.returncode == expected_tool_exit, (
f"Diagnostic tool failed: {result_exec.stderr}"
)
if result_exec.returncode != expected_tool_exit:
ensure_logs("diagnostic exit code mismatch")
pytest.fail(
f"Diagnostic tool failed (expected {expected_tool_exit}, got {result_exec.returncode}): {result_exec.stderr}"
)
if test_scenario.expected_issues:
validate_scenario_table_output(diagnostic_output, test_scenario)
@@ -400,7 +556,9 @@ def test_mount_diagnostic(netalertx_test_image, test_scenario):
)
else:
# Should have table output but no warning message
assert "Path" in result_exec.stdout, f"Good config {test_scenario.name} should show table, got: {result_exec.stdout}"
assert "Path" in result_exec.stdout, (
f"Good config {test_scenario.name} should show table, got: {result_exec.stdout}"
)
assert "⚠️" not in diagnostic_output, (
f"Good config {test_scenario.name} should not show warning, got stderr: {result_exec.stderr}"
)
@@ -408,26 +566,41 @@ def test_mount_diagnostic(netalertx_test_image, test_scenario):
finally:
# Stop container
cmd_down = [
"docker-compose", "-f", str(compose_file),
"-p", project_name, "down", "-v"
]
subprocess.run(cmd_down, capture_output=True, timeout=30)
subprocess.run(
base_cmd + ["down", "-v"], capture_output=True, timeout=30, env=compose_env
)
def test_table_parsing():
"""Test the table parsing and assertion functions."""
sample_output = """
Path | Writeable | Mount | RAMDisk | Performance | DataLoss
------------------------------------+-----------+-------+---------+-------------+----------
/app/db | ✅ | ❌ | | | ❌
/app/api | ✅ | ✅ | ✅ | ✅ | ✅
Path | Writeable | Mount | RAMDisk | Performance | DataLoss
---------------------+-----------+-------+---------+-------------+----------
/data/db | ✅ | ❌ | | | ❌
/tmp/api | ✅ | ✅ | ✅ | ✅ | ✅
"""
# Test parsing
rows = parse_mount_table(sample_output)
assert len(rows) == 2
# Test assertions
assert_table_row(sample_output, "/app/db", writeable=True, mount=False, ramdisk=None, performance=None, dataloss=False)
assert_table_row(sample_output, "/app/api", writeable=True, mount=True, ramdisk=True, performance=True, dataloss=True)
assert_table_row(
sample_output,
"/data/db",
writeable=True,
mount=False,
ramdisk=None,
performance=None,
dataloss=False,
)
assert_table_row(
sample_output,
CONTAINER_PATHS["api"],
writeable=True,
mount=True,
ramdisk=True,
performance=True,
dataloss=True,
)

View File

@@ -12,13 +12,14 @@ import pytest
IMAGE = os.environ.get("NETALERTX_TEST_IMAGE", "netalertx-test")
GRACE_SECONDS = float(os.environ.get("NETALERTX_TEST_GRACE", "2"))
VOLUME_MAP = {
"app_db": "/app/db",
"app_config": "/app/config",
"app_log": "/app/log",
"app_api": "/app/api",
"nginx_conf": "/services/config/nginx/conf.active",
"services_run": "/services/run",
CONTAINER_TARGETS = {
"data": "/data",
"app_db": "/data/db",
"app_config": "/data/config",
"app_log": "/tmp/log",
"app_api": os.environ.get("NETALERTX_API", "/tmp/api"),
"nginx_conf": "/tmp/nginx/active-config",
"services_run": "/tmp/run",
}
pytestmark = [pytest.mark.docker, pytest.mark.feature_complete]
@@ -58,7 +59,6 @@ def dummy_container(tmp_path):
def _setup_mount_tree(tmp_path: pathlib.Path, label: str) -> dict[str, pathlib.Path]:
"""Set up mount tree for testing."""
import uuid
import shutil
base = tmp_path / f"{label}_mount_root"
@@ -66,23 +66,41 @@ def _setup_mount_tree(tmp_path: pathlib.Path, label: str) -> dict[str, pathlib.P
shutil.rmtree(base)
base.mkdir(parents=True)
paths = {}
for key, target in VOLUME_MAP.items():
folder_name = f"{label}_{key.upper()}_INTENTIONAL_NETALERTX_TEST"
host_path = base / folder_name
host_path.mkdir(parents=True, exist_ok=True)
host_path.chmod(0o777)
paths[key] = host_path
paths: dict[str, pathlib.Path] = {}
data_root = base / f"{label}_DATA_INTENTIONAL_NETALERTX_TEST"
data_root.mkdir(parents=True, exist_ok=True)
data_root.chmod(0o777)
paths["data"] = data_root
db_dir = data_root / "db"
db_dir.mkdir(exist_ok=True)
db_dir.chmod(0o777)
paths["app_db"] = db_dir
config_dir = data_root / "config"
config_dir.mkdir(exist_ok=True)
config_dir.chmod(0o777)
paths["app_config"] = config_dir
# Seed config and database from repository defaults when available
repo_root = pathlib.Path(__file__).resolve().parents[2]
config_src = repo_root / "back" / "app.conf"
db_src = repo_root / "db" / "app.db"
if config_src.exists():
shutil.copyfile(config_src, config_dir / "app.conf")
(config_dir / "app.conf").chmod(0o600)
if db_src.exists():
shutil.copyfile(db_src, db_dir / "app.db")
(db_dir / "app.db").chmod(0o600)
return paths
def _build_volume_args(paths: dict[str, pathlib.Path]) -> list[tuple[str, str, bool]]:
"""Build volume arguments for docker run."""
bindings = []
for key, target in VOLUME_MAP.items():
bindings.append((str(paths[key]), target, False))
return bindings
return [(str(paths["data"]), CONTAINER_TARGETS["data"], False)]
def _run_container(

View File

@@ -1,9 +1,10 @@
import sys
import base64
import random
import os
import pytest
INSTALL_PATH = "/app"
INSTALL_PATH = os.getenv('NETALERTX_APP', '/app')
sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"])
from helper import get_setting_value, timeNowTZ

View File

@@ -4,31 +4,37 @@ import sqlite3
import random
import string
import uuid
import os
import pytest
INSTALL_PATH = "/app"
INSTALL_PATH = os.getenv("NETALERTX_APP", "/app")
sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"])
from helper import timeNowTZ, get_setting_value
from api_server.api_server_start import app
@pytest.fixture(scope="session")
def api_token():
return get_setting_value("API_TOKEN")
@pytest.fixture
def client():
with app.test_client() as client:
yield client
@pytest.fixture
def test_mac():
# Generate a unique MAC for each test run
return "AA:BB:CC:" + ":".join(f"{random.randint(0,255):02X}" for _ in range(3))
def auth_headers(token):
return {"Authorization": f"Bearer {token}"}
def test_create_device(client, api_token, test_mac):
payload = {
"createNew": True,
@@ -37,49 +43,74 @@ def test_create_device(client, api_token, test_mac):
"devType": "Router",
"devVendor": "TestVendor",
}
resp = client.post(f"/device/{test_mac}", json=payload, headers=auth_headers(api_token))
resp = client.post(
f"/device/{test_mac}", json=payload, headers=auth_headers(api_token)
)
assert resp.status_code == 200
assert resp.json.get("success") is True
def test_get_device(client, api_token, test_mac):
# First create it
client.post(f"/device/{test_mac}", json={"createNew": True}, headers=auth_headers(api_token))
client.post(
f"/device/{test_mac}", json={"createNew": True}, headers=auth_headers(api_token)
)
# Then retrieve it
resp = client.get(f"/device/{test_mac}", headers=auth_headers(api_token))
assert resp.status_code == 200
assert resp.json.get("devMac") == test_mac
def test_reset_device_props(client, api_token, test_mac):
client.post(f"/device/{test_mac}", json={"createNew": True}, headers=auth_headers(api_token))
resp = client.post(f"/device/{test_mac}/reset-props", json={}, headers=auth_headers(api_token))
client.post(
f"/device/{test_mac}", json={"createNew": True}, headers=auth_headers(api_token)
)
resp = client.post(
f"/device/{test_mac}/reset-props", json={}, headers=auth_headers(api_token)
)
assert resp.status_code == 200
assert resp.json.get("success") is True
def test_delete_device_events(client, api_token, test_mac):
client.post(f"/device/{test_mac}", json={"createNew": True}, headers=auth_headers(api_token))
resp = client.delete(f"/device/{test_mac}/events/delete", headers=auth_headers(api_token))
client.post(
f"/device/{test_mac}", json={"createNew": True}, headers=auth_headers(api_token)
)
resp = client.delete(
f"/device/{test_mac}/events/delete", headers=auth_headers(api_token)
)
assert resp.status_code == 200
assert resp.json.get("success") is True
def test_delete_device(client, api_token, test_mac):
client.post(f"/device/{test_mac}", json={"createNew": True}, headers=auth_headers(api_token))
client.post(
f"/device/{test_mac}", json={"createNew": True}, headers=auth_headers(api_token)
)
resp = client.delete(f"/device/{test_mac}/delete", headers=auth_headers(api_token))
assert resp.status_code == 200
assert resp.json.get("success") is True
def test_copy_device(client, api_token, test_mac):
# Step 1: Create the source device
payload = {"createNew": True}
resp = client.post(f"/device/{test_mac}", json=payload, headers=auth_headers(api_token))
resp = client.post(
f"/device/{test_mac}", json=payload, headers=auth_headers(api_token)
)
assert resp.status_code == 200
assert resp.json.get("success") is True
# Step 2: Generate a target MAC
target_mac = "AA:BB:CC:" + ":".join(f"{random.randint(0,255):02X}" for _ in range(3))
target_mac = "AA:BB:CC:" + ":".join(
f"{random.randint(0,255):02X}" for _ in range(3)
)
# Step 3: Copy device
copy_payload = {"macFrom": test_mac, "macTo": target_mac}
resp = client.post("/device/copy", json=copy_payload, headers=auth_headers(api_token))
resp = client.post(
"/device/copy", json=copy_payload, headers=auth_headers(api_token)
)
assert resp.status_code == 200
assert resp.json.get("success") is True
@@ -92,6 +123,7 @@ def test_copy_device(client, api_token, test_mac):
client.delete(f"/device/{test_mac}/delete", headers=auth_headers(api_token))
client.delete(f"/device/{target_mac}/delete", headers=auth_headers(api_token))
def test_update_device_column(client, api_token, test_mac):
# First, create the device
client.post(

View File

@@ -5,9 +5,10 @@ import base64
import random
import string
import uuid
import os
import pytest
INSTALL_PATH = "/app"
INSTALL_PATH = os.getenv('NETALERTX_APP', '/app')
sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"])
from helper import timeNowTZ, get_setting_value

View File

@@ -4,10 +4,11 @@ import sqlite3
import random
import string
import uuid
import os
import pytest
from datetime import datetime, timedelta
INSTALL_PATH = "/app"
INSTALL_PATH = os.getenv('NETALERTX_APP', '/app')
sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"])
from helper import timeNowTZ, get_setting_value

View File

@@ -4,10 +4,11 @@ import sqlite3
import random
import string
import uuid
import os
import pytest
from datetime import datetime, timedelta
INSTALL_PATH = "/app"
INSTALL_PATH = os.getenv('NETALERTX_APP', '/app')
sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"])
from helper import timeNowTZ, get_setting_value

View File

@@ -4,32 +4,38 @@ import sqlite3
import random
import string
import uuid
import os
import pytest
INSTALL_PATH = "/app"
INSTALL_PATH = os.getenv("NETALERTX_APP", "/app")
sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"])
from helper import timeNowTZ, get_setting_value
from api_server.api_server_start import app
@pytest.fixture(scope="session")
def api_token():
return get_setting_value("API_TOKEN")
@pytest.fixture
def client():
with app.test_client() as client:
yield client
@pytest.fixture
def test_mac():
# Generate a unique MAC for each test run
return "AA:BB:CC:" + ":".join(f"{random.randint(0,255):02X}" for _ in range(3))
def auth_headers(token):
return {"Authorization": f"Bearer {token}"}
def test_delete_history(client, api_token):
resp = client.delete(f"/history", headers=auth_headers(api_token))
assert resp.status_code == 200
assert resp.json.get("success") is True
assert resp.json.get("success") is True

View File

@@ -11,7 +11,7 @@ import os
import sys
# Define the installation path and extend the system path for plugin imports
INSTALL_PATH = "/app"
INSTALL_PATH = os.getenv('NETALERTX_APP', '/app')
sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"])
from api_server.api_server_start import app

View File

@@ -5,9 +5,10 @@ import base64
import random
import string
import uuid
import os
import pytest
INSTALL_PATH = "/app"
INSTALL_PATH = os.getenv('NETALERTX_APP', '/app')
sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"])
from helper import timeNowTZ, get_setting_value

View File

@@ -4,10 +4,11 @@ import sqlite3
import random
import string
import uuid
import os
import pytest
from datetime import datetime, timedelta
INSTALL_PATH = "/app"
INSTALL_PATH = os.getenv('NETALERTX_APP', '/app')
sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"])
from helper import timeNowTZ, get_setting_value
@@ -175,7 +176,7 @@ def test_delete_session(client, api_token, test_mac):
# Confirm deletion
resp = client.get(f"/sessions/list?mac={test_mac}", headers=auth_headers(api_token))
sessions = resp.json.get("sessions")
assert not any(ev["ses_MAC"] == test_mac for ses in sessions)
assert not any(ses["ses_MAC"] == test_mac for ses in sessions)

View File

@@ -4,10 +4,11 @@ import sqlite3
import random
import string
import uuid
import os
import pytest
from datetime import datetime, timedelta
INSTALL_PATH = "/app"
INSTALL_PATH = os.getenv('NETALERTX_APP', '/app')
sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"])
from helper import timeNowTZ, get_setting_value

View File

@@ -16,7 +16,7 @@ import os
from unittest.mock import Mock, patch, MagicMock
# Add the server directory to the path for imports
INSTALL_PATH = "/app"
INSTALL_PATH = os.getenv('NETALERTX_APP', '/app')
sys.path.extend([f"{INSTALL_PATH}/server"])
sys.path.append('/home/dell/coding/bash/10x-agentic-setup/netalertx-sql-fix/server')
@@ -339,7 +339,11 @@ class TestSecurityBenchmarks(unittest.TestCase):
def test_memory_usage_parameter_generation(self):
"""Test memory usage of parameter generation."""
import psutil
try:
import psutil
except ImportError: # pragma: no cover - optional dependency
self.skipTest("psutil not available")
return
import os
process = psutil.Process(os.getpid())