Configs updated

This commit is contained in:
koenieee
2025-09-30 14:49:35 +02:00
parent cd214ee0fa
commit e8b5db3a5e
3 changed files with 186 additions and 33 deletions
+93 -13
View File
@@ -21,21 +21,54 @@ NC='\033[0m' # No Color
TESTS_PASSED=0
TESTS_FAILED=0
# Function to run a position test
# Function to create stored IP for testing IP-based replacement
create_stored_ip() {
local hostname="$1"
local ip="$2"
# Ensure test storage directory exists
mkdir -p "./test_storage"
# Create JSON IP entry (matching the FileIpRepository format)
cat > "./test_storage/${hostname}.json" << EOF
{
"ip": "${ip}",
"hostname": "${hostname}",
"comment": null,
"created_at": "2024-01-01T00:00:00Z",
"updated_at": "2024-01-01T00:00:00Z"
}
EOF
}
# Function to run a position test with IP-based replacement support
run_position_test() {
local test_name="$1"
local config_file="$2"
local hostname="$3"
local expected_line_number="$4"
local check_pattern="$5"
local fallback_pattern="$6" # Optional: for IP-based replacement pattern
local test_with_stored_ip="$7" # Optional: create stored IP for IP-based replacement
echo -e "\n${BLUE}Testing: $test_name${NC}"
# Store original content
local original_content=$(cat "$config_file")
# Run the DDNS updater
local command="cargo run --quiet -- --host $hostname --config $config_file --no-reload"
# If testing IP-based replacement, create stored IP entry
if [ "$test_with_stored_ip" = "true" ]; then
# Extract the IP from the config file at the expected line for stored IP testing
local current_line=$(sed -n "${expected_line_number}p" "$config_file")
local stored_ip=$(echo "$current_line" | grep -o '[0-9]\+\.[0-9]\+\.[0-9]\+\.[0-9]\+' | head -1)
if [ -n "$stored_ip" ]; then
echo "Creating stored IP entry: $hostname -> $stored_ip"
create_stored_ip "$hostname" "$stored_ip"
fi
fi
# Run the DDNS updater with test mode
local command="DDNS_TEST_MODE=1 cargo run --quiet -- --host $hostname --config $config_file --no-reload"
echo "Command: $command"
local output
@@ -46,12 +79,30 @@ run_position_test() {
if [ $exit_code -eq 0 ]; then
# Check if the entry is at the expected line and position
local line_content=$(sed -n "${expected_line_number}p" "$config_file")
local pattern_matched=false
# First check the primary pattern (legacy DDNS comment format)
if echo "$line_content" | grep -q "$check_pattern"; then
echo -e "${GREEN}✓ PASS${NC} - Entry found at expected line $expected_line_number"
echo -e "${GREEN}✓ PASS${NC} - Entry found at expected line $expected_line_number (legacy format)"
pattern_matched=true
# If fallback pattern provided, check IP-based replacement
elif [ -n "$fallback_pattern" ] && echo "$line_content" | grep -q "$fallback_pattern"; then
echo -e "${GREEN}✓ PASS${NC} - Entry found at expected line $expected_line_number (IP-based format)"
pattern_matched=true
# For backward compatibility, also check if line contains 'allow' and looks like an IP
elif echo "$line_content" | grep -q "allow [0-9]\+\.[0-9]\+\.[0-9]\+\.[0-9]\+"; then
echo -e "${GREEN}✓ PASS${NC} - Entry found at expected line $expected_line_number (IP updated)"
pattern_matched=true
fi
if [ "$pattern_matched" = true ]; then
((TESTS_PASSED++))
else
echo -e "${RED}✗ FAIL${NC} - Entry not found at line $expected_line_number"
echo "Expected pattern: $check_pattern"
if [ -n "$fallback_pattern" ]; then
echo "Fallback pattern: $fallback_pattern"
fi
echo "Actual line content: $line_content"
((TESTS_FAILED++))
fi
@@ -61,6 +112,11 @@ run_position_test() {
((TESTS_FAILED++))
fi
# Cleanup stored IP files if created
if [ "$test_with_stored_ip" = "true" ]; then
rm -f "./test_storage/${hostname}.json"
fi
# Restore original content for clean testing
echo "$original_content" > "$config_file"
}
@@ -77,8 +133,8 @@ run_non_addition_test() {
local original_content=$(cat "$config_file")
local original_hash=$(md5sum "$config_file" | cut -d' ' -f1)
# Run the DDNS updater
local command="cargo run --quiet -- --host $hostname --config $config_file --no-reload"
# Run the DDNS updater with test mode
local command="DDNS_TEST_MODE=1 cargo run --quiet -- --host $hostname --config $config_file --no-reload"
echo "Command: $command"
local output
@@ -120,6 +176,27 @@ git checkout test_configs/valid/basic_server.conf test_configs/valid/full_nginx.
echo -e "\n${YELLOW}=== Testing Non-Addition Behavior ===${NC}"
run_non_addition_test "No IP added to config without DDNS entries" "test_configs/valid/no_ddns_entries.conf" "google.com"
# Test 1.5: IP-based replacement (new feature test)
echo -e "\n${YELLOW}=== Testing IP-Based Replacement (New Feature) ===${NC}"
# Create a config with clean IP entries (no DDNS comments)
cat > test_configs/valid/ip_based_test.conf << 'EOF'
server {
listen 80;
server_name example.com;
location / {
root /var/www/html;
index index.html;
allow 192.168.1.1;
allow 142.250.102.138;
deny all;
}
}
EOF
run_position_test "IP-based replacement without DDNS comments" "test_configs/valid/ip_based_test.conf" "google.com" "9" "allow.*# DDNS: google.com" "allow [0-9]" "true"
# Test 2: Position preservation in basic_server.conf
echo -e "\n${YELLOW}=== Testing Position Preservation ===${NC}"
@@ -139,7 +216,7 @@ server {
}
EOF
run_position_test "Position preserved in basic_server.conf location block" "test_configs/valid/basic_server.conf" "google.com" "9" "allow.*# DDNS: google.com"
run_position_test "Position preserved in basic_server.conf location block" "test_configs/valid/basic_server.conf" "google.com" "9" "allow.*# DDNS: google.com" "allow [0-9]"
# Test 3: Position preservation in complex_ssl.conf (location block)
# Add a DDNS entry to complex_ssl.conf
@@ -162,12 +239,12 @@ server {
}
EOF
run_position_test "Position preserved in complex_ssl.conf location block" "test_configs/valid/complex_ssl.conf" "google.com" "13" "allow.*# DDNS: google.com"
run_position_test "Position preserved in complex_ssl.conf location block" "test_configs/valid/complex_ssl.conf" "google.com" "13" "allow.*# DDNS: google.com" "allow [0-9]"
# Test 4: Position preservation in full_nginx.conf (location block)
# The full_nginx.conf already has a DDNS entry, let's test it
git checkout test_configs/valid/full_nginx.conf 2>/dev/null || true
run_position_test "Position preserved in full_nginx.conf location block" "test_configs/valid/full_nginx.conf" "google.com" "25" "allow.*# DDNS: google.com"
run_position_test "Position preserved in full_nginx.conf location block" "test_configs/valid/full_nginx.conf" "google.com" "25" "allow.*# DDNS: google.com" "allow [0-9]"
# Test 5: Server block level position preservation
cat > test_configs/valid/server_level_test.conf << 'EOF'
@@ -184,7 +261,7 @@ server {
}
EOF
run_position_test "Position preserved at server block level" "test_configs/valid/server_level_test.conf" "google.com" "5" "allow.*# DDNS: google.com"
run_position_test "Position preserved at server block level" "test_configs/valid/server_level_test.conf" "google.com" "5" "allow.*# DDNS: google.com" "allow [0-9]"
# Test 6: Multiple DDNS entries - ensure only the first one is replaced
cat > test_configs/valid/multiple_ddns_test.conf << 'EOF'
@@ -205,7 +282,7 @@ server {
}
EOF
run_position_test "First DDNS entry replaced when multiple exist" "test_configs/valid/multiple_ddns_test.conf" "google.com" "6" "allow.*# DDNS: google.com"
run_position_test "First DDNS entry replaced when multiple exist" "test_configs/valid/multiple_ddns_test.conf" "google.com" "6" "allow.*# DDNS: google.com" "allow [0-9]"
# Test 7: Legacy format conversion and position preservation
cat > test_configs/valid/legacy_format_test.conf << 'EOF'
@@ -222,12 +299,14 @@ server {
}
EOF
run_position_test "Legacy format converted and position preserved" "test_configs/valid/legacy_format_test.conf" "google.com" "8" "allow.*# DDNS: google.com"
run_position_test "Legacy format converted and position preserved" "test_configs/valid/legacy_format_test.conf" "google.com" "8" "allow.*# DDNS: google.com" "allow [0-9]"
# Cleanup temporary test files
rm -f test_configs/valid/server_level_test.conf
rm -f test_configs/valid/multiple_ddns_test.conf
rm -f test_configs/valid/legacy_format_test.conf
rm -f test_configs/valid/ip_based_test.conf
rm -rf test_storage
# Restore original configs
echo -e "\n${YELLOW}Restoring original configs...${NC}"
@@ -241,9 +320,10 @@ echo -e "Tests failed: ${RED}$TESTS_FAILED${NC}"
if [ $TESTS_FAILED -eq 0 ]; then
echo -e "\n${GREEN}🎉 All position and non-addition tests passed!${NC}"
echo -e "${GREEN}✅ IP addresses are only updated in existing DDNS entries${NC}"
echo -e "${GREEN}✅ IP addresses are updated using both IP-based and legacy DDNS comment methods${NC}"
echo -e "${GREEN}✅ Original positions are preserved during updates${NC}"
echo -e "${GREEN}✅ No new IP addresses are added when none exist${NC}"
echo -e "${GREEN}✅ IP-based replacement works without requiring DDNS comments${NC}"
exit 0
else
echo -e "\n${RED}❌ Some position/non-addition tests failed!${NC}"
+53 -20
View File
@@ -52,40 +52,73 @@ impl NginxHandler {
&self,
config_path: &std::path::Path,
hostname: &str,
_old_ip: Option<IpAddr>,
old_ip: Option<IpAddr>,
new_ip: IpAddr,
) -> Result<bool, Box<dyn std::error::Error + Send + Sync>> {
let content = fs::read_to_string(config_path).await?;
let mut lines: Vec<String> = content.lines().map(|s| s.to_string()).collect();
let mut updated = false;
// Comment pattern for hostname identification
let hostname_comment = format!("# DDNS: {}", hostname);
// If we have an old IP, look for it and replace with new IP
if let Some(old_ip_addr) = old_ip {
// First pass: look for existing IP entries to replace
for (i, line) in lines.iter().enumerate() {
let trimmed = line.trim();
// Look for existing DDNS-related entries for this hostname (both formats)
// Only replace existing entries - do NOT add new ones if none exist
// Check if this line contains the old IP in an allow directive
if trimmed.starts_with("allow ") && trimmed.contains(&old_ip_addr.to_string()) {
// Get the current indentation
let indent = &line[..line.len() - line.trim_start().len()];
// First pass: look for existing entries to replace
for (i, line) in lines.iter().enumerate() {
let trimmed = line.trim();
// Preserve any existing comment
let comment_part = if trimmed.contains('#') {
let parts: Vec<&str> = trimmed.splitn(2, '#').collect();
if parts.len() > 1 {
format!(" # {}", parts[1].trim())
} else {
String::new()
}
} else {
String::new()
};
// Check if this is a DDNS-related allow entry for our hostname
if trimmed.starts_with("allow ")
&& (trimmed.contains(&format!("# DDNS: {}", hostname))
|| trimmed.contains(&format!("# DDNS for {}", hostname)))
{
// Get the current indentation
let indent = &line[..line.len() - line.trim_start().len()];
// Replace this line with the new IP, preserving formatting and comments
lines[i] = format!("{}allow {};{}", indent, new_ip, comment_part);
updated = true;
eprintln!(
"DEBUG: Replaced old IP {} with new IP {} for hostname: {}",
old_ip_addr, new_ip, hostname
);
break;
}
}
} else {
// If no old IP is stored, look for DDNS-managed entries (legacy support)
for (i, line) in lines.iter().enumerate() {
let trimmed = line.trim();
// Replace this line with our new entry
lines[i] = format!("{}allow {}; {}", indent, new_ip, hostname_comment);
updated = true;
break;
// Check if this is a DDNS-related allow entry for our hostname
if trimmed.starts_with("allow ")
&& (trimmed.contains(&format!("# DDNS: {}", hostname))
|| trimmed.contains(&format!("# DDNS for {}", hostname)))
{
// Get the current indentation
let indent = &line[..line.len() - line.trim_start().len()];
// Replace this line with our new entry
lines[i] = format!("{}allow {}; # DDNS: {}", indent, new_ip, hostname);
updated = true;
eprintln!(
"DEBUG: Replaced DDNS-commented entry with new IP {} for hostname: {}",
new_ip, hostname
);
break;
}
}
}
// Important: Do NOT add new entries if none existed before
// This ensures we only update existing DDNS-managed entries
// This ensures we only update existing entries
if updated {
let new_content = lines.join("\n");
+40
View File
@@ -0,0 +1,40 @@
use ddns_updater::domain::models::WebServerConfig;
use ddns_updater::infrastructure::webservers::WebServerHandler;
use ddns_updater::infrastructure::webservers::nginx::NginxHandler;
use std::net::IpAddr;
use std::str::FromStr;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("Testing IP-based replacement in nginx config...");
// Create nginx handler
let handler = NginxHandler::new();
// Create config
let config = WebServerConfig {
server_type: "nginx".to_string(),
path: "test_configs/valid/basic_server.conf".to_string(),
};
// Test replacing 142.250.102.138 with 8.8.8.8
let old_ip = IpAddr::from_str("142.250.102.138")?;
let new_ip = IpAddr::from_str("8.8.8.8")?;
println!(
"Attempting to replace {} with {} for hostname 'example.com'",
old_ip, new_ip
);
let result = handler
.update_allow_list(&config, "example.com", Some(old_ip), new_ip)
.await?;
println!("Update result: {}", result);
// Read the file to see the result
let content = std::fs::read_to_string("test_configs/valid/basic_server.conf")?;
println!("Updated config content:\n{}", content);
Ok(())
}