mirror of
https://github.com/gesellix/Bose-SoundTouch.git
synced 2026-08-19 17:16:16 +00:00
Add verification steps to speaker migration process
This update adds explicit verification checks after applying changes via XML, Hosts, and ResolvConf migration methods. The service now verifies that configuration files are correctly updated on the device before considering the migration successful, preventing unreliable states.
This commit is contained in:
@@ -283,7 +283,7 @@ The most robust and flexible DNS-based migration method. It utilizes the device'
|
||||
# Patch udhcpc.script if it exists (e.g. SoundTouch 10)
|
||||
TARGET_SCRIPT="/opt/Bose/udhcpc.script"
|
||||
if [ -f "$TARGET_SCRIPT" ] && ! grep -q "$HOOK_MARKER" "$TARGET_SCRIPT"; then
|
||||
sed -i '/echo "search \$search_list # \$interface" >> \$RESOLV_CONF/a \ [ -f '"$HOOK_MARKER"' ] && cat '"$HOOK_MARKER"' >> '"$RESOLV_CONF"' && dns=""' "$TARGET_SCRIPT"
|
||||
sed -i '/echo "search \$search_list # \$interface" >> \$RESOLV_CONF/a \ [ -f '"$HOOK_MARKER"' ] && cat '"$HOOK_MARKER"' >> '"\$RESOLV_CONF"' && dns=""' "$TARGET_SCRIPT"
|
||||
fi
|
||||
fi
|
||||
```
|
||||
|
||||
@@ -713,6 +713,16 @@ func (m *Manager) migrateViaXML(deviceIP, targetURL, proxyURL string, options ma
|
||||
|
||||
logs += "Uploaded new configuration to " + remotePath + "\n"
|
||||
|
||||
// 2. Verify the configuration on device
|
||||
if verification, err := client.Run(fmt.Sprintf("cat %s", remotePath)); err == nil {
|
||||
if !strings.Contains(verification, cfg.MargeServerUrl) {
|
||||
return logs, fmt.Errorf("verification failed: uploaded config on %s does not contain expected margeServerUrl", deviceIP)
|
||||
}
|
||||
logs += "Verified configuration on device\n"
|
||||
} else {
|
||||
logs += fmt.Sprintf("Warning: could not verify configuration on device: %v\n", err)
|
||||
}
|
||||
|
||||
return logs, nil
|
||||
}
|
||||
|
||||
@@ -1036,9 +1046,21 @@ func (m *Manager) migrateViaHosts(deviceIP, targetURL string) (string, error) {
|
||||
|
||||
logs += "Uploaded updated /etc/hosts\n"
|
||||
|
||||
// 4. Verify /etc/hosts on device
|
||||
if verification, err := client.Run("cat /etc/hosts"); err == nil {
|
||||
for _, domain := range domains {
|
||||
if !strings.Contains(verification, domain) || !strings.Contains(verification, hostIP) {
|
||||
return logs, fmt.Errorf("verification failed: /etc/hosts on %s does not contain expected redirection for %s", deviceIP, domain)
|
||||
}
|
||||
}
|
||||
logs += "Verified /etc/hosts on device\n"
|
||||
} else {
|
||||
logs += fmt.Sprintf("Warning: could not verify /etc/hosts on device: %v\n", err)
|
||||
}
|
||||
|
||||
fmt.Printf("Updated /etc/hosts on %s:\n%s\n", deviceIP, hostsContent)
|
||||
|
||||
// 4. Inject CA Certificate
|
||||
// 5. Inject CA Certificate
|
||||
summary := &MigrationSummary{}
|
||||
m.checkCACertTrusted(summary, deviceIP)
|
||||
|
||||
@@ -1112,7 +1134,7 @@ if [ -f "%s" ]; then
|
||||
targetScript="/opt/Bose/udhcpc.script"
|
||||
if [ -f "$targetScript" ] && ! grep -q "%s" "$targetScript"; then
|
||||
logger -t "aftertouch" "Patching $targetScript with Aftertouch DNS hook"
|
||||
sed -i '/echo "search \$search_list # \$interface" >> \$RESOLV_CONF/a \ [ -f '"%s"' ] && cat '"%s"' >> '"$RESOLV_CONF"' && dns=""' "$targetScript"
|
||||
sed -i '/echo "search \$search_list # \$interface" >> \$RESOLV_CONF/a \ [ -f '"%s"' ] && cat '"%s"' >> '"\$RESOLV_CONF"' && dns=""' "$targetScript"
|
||||
fi
|
||||
fi
|
||||
`, hookMarker, targetDHCPFile, hookMarker, targetDHCPFile, targetDHCPFile, hookMarker, hookMarker, targetDHCPFile, hookMarker, hookMarker, hookMarker)
|
||||
@@ -1165,6 +1187,13 @@ fi
|
||||
logs += fmt.Sprintf("Failed to apply patch immediately to %s: %v\n", targetDHCPFile, err)
|
||||
} else {
|
||||
logs += fmt.Sprintf("Applied patch to %s\n", targetDHCPFile)
|
||||
|
||||
// Verify patch on 50default
|
||||
if verification, err := client.Run(fmt.Sprintf("grep -q \"%s\" %s && echo \"OK\"", hookMarker, targetDHCPFile)); err == nil && strings.TrimSpace(verification) == "OK" {
|
||||
logs += fmt.Sprintf("Verified patch on %s\n", targetDHCPFile)
|
||||
} else {
|
||||
logs += fmt.Sprintf("Warning: could not verify patch on %s: %v\n", targetDHCPFile, err)
|
||||
}
|
||||
}
|
||||
|
||||
// Apply patch immediately to /opt/Bose/udhcpc.script if it exists
|
||||
@@ -1179,11 +1208,18 @@ fi
|
||||
_, _ = client.Run(fmt.Sprintf("cp %s.original %s", targetScript, targetScript))
|
||||
}
|
||||
|
||||
patchCmdScript := fmt.Sprintf("sed -i '/echo \"search \\$search_list # \\$interface\" >> \\$RESOLV_CONF/a \\ [ -f '\"%s\"' ] && cat '\"%s\"' >> '\"$RESOLV_CONF\"' && dns=\"\"' %s", hookMarker, hookMarker, targetScript)
|
||||
patchCmdScript := fmt.Sprintf("sed -i '/echo \"search \\$search_list # \\$interface\" >> \\$RESOLV_CONF/a \\ [ -f '\"%s\"' ] && cat '\"%s\"' >> '\"\\$RESOLV_CONF\"' && dns=\"\"' %s", hookMarker, hookMarker, targetScript)
|
||||
if _, err := client.Run(patchCmdScript); err != nil {
|
||||
logs += fmt.Sprintf("Failed to apply patch immediately to %s: %v\n", targetScript, err)
|
||||
} else {
|
||||
logs += fmt.Sprintf("Applied patch to %s\n", targetScript)
|
||||
|
||||
// Verify patch on udhcpc.script
|
||||
if verification, err := client.Run(fmt.Sprintf("grep -q \"%s\" %s && echo \"OK\"", hookMarker, targetScript)); err == nil && strings.TrimSpace(verification) == "OK" {
|
||||
logs += fmt.Sprintf("Verified patch on %s\n", targetScript)
|
||||
} else {
|
||||
logs += fmt.Sprintf("Warning: could not verify patch on %s: %v\n", targetScript, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -52,6 +52,10 @@ func TestMigrateViaHosts(t *testing.T) {
|
||||
runFunc: func(command string) (string, error) {
|
||||
runCalls = append(runCalls, command)
|
||||
if command == "cat /etc/hosts" {
|
||||
// Handle both initial read and verification read
|
||||
if len(runCalls) > 2 { // Rough heuristic: verification happens after upload
|
||||
return "192.168.1.100\tstreaming.bose.com\n192.168.1.100\tupdates.bose.com\n192.168.1.100\tstats.bose.com\n192.168.1.100\tbmx.bose.com\n192.168.1.100\tcontent.api.bose.io\n192.168.1.100\tevents.api.bosecm.com\n192.168.1.100\tbose-prod.apigee.net\n192.168.1.100\tworldwide.bose.com", nil
|
||||
}
|
||||
return "127.0.0.1 localhost", nil
|
||||
}
|
||||
if strings.HasPrefix(command, "[ -f") {
|
||||
@@ -122,9 +126,14 @@ func TestMigrateViaHosts_UpdateExisting(t *testing.T) {
|
||||
m := NewManager("http://192.168.1.100:8000", nil, cm)
|
||||
|
||||
m.NewSSH = func(host string) SSHClient {
|
||||
runCount := 0
|
||||
return &mockSSH{
|
||||
runFunc: func(command string) (string, error) {
|
||||
runCount++
|
||||
if command == "cat /etc/hosts" {
|
||||
if runCount > 1 {
|
||||
return "127.0.0.1 localhost\n192.168.1.100\tstreaming.bose.com\n192.168.1.100\tupdates.bose.com\n192.168.1.100\tstats.bose.com\n192.168.1.100\tbmx.bose.com\n192.168.1.100\tcontent.api.bose.io\n192.168.1.100\tevents.api.bosecm.com\n192.168.1.100\tbose-prod.apigee.net\n192.168.1.100\tworldwide.bose.com", nil
|
||||
}
|
||||
return "127.0.0.1 localhost\n1.2.3.4\tstreaming.bose.com\n1.2.3.4\tupdates.bose.com", nil
|
||||
}
|
||||
if strings.HasPrefix(command, "[ -f") {
|
||||
@@ -598,6 +607,10 @@ func TestMigrateViaHosts_SkipCAIfTrusted(t *testing.T) {
|
||||
runFunc: func(command string) (string, error) {
|
||||
runCalls = append(runCalls, command)
|
||||
if command == "cat /etc/hosts" {
|
||||
// Handle both initial read and verification read
|
||||
if len(runCalls) > 2 { // Rough heuristic: verification happens after upload
|
||||
return "192.168.1.100\tstreaming.bose.com\n192.168.1.100\tupdates.bose.com\n192.168.1.100\tstats.bose.com\n192.168.1.100\tbmx.bose.com\n192.168.1.100\tcontent.api.bose.io\n192.168.1.100\tevents.api.bosecm.com\n192.168.1.100\tbose-prod.apigee.net\n192.168.1.100\tworldwide.bose.com", nil
|
||||
}
|
||||
return "127.0.0.1 localhost", nil
|
||||
}
|
||||
if strings.HasPrefix(command, "grep -F") {
|
||||
@@ -1115,6 +1128,9 @@ func TestMigrateViaResolvConf(t *testing.T) {
|
||||
if command == "cat /mnt/nv/rc.local" {
|
||||
return "#!/bin/sh\n", nil
|
||||
}
|
||||
if strings.HasPrefix(command, "grep -q \"/mnt/nv/aftertouch.resolv.conf\"") {
|
||||
return "OK", nil
|
||||
}
|
||||
if strings.HasPrefix(command, "[ -f") {
|
||||
return "", fmt.Errorf("file not found")
|
||||
}
|
||||
@@ -1177,6 +1193,9 @@ func TestMigrateViaResolvConf_CorruptedRcLocal(t *testing.T) {
|
||||
// Simulate corrupted file containing error message
|
||||
return "cat: can't open '/mnt/nv/rc.local': No such file or directory", nil
|
||||
}
|
||||
if strings.HasPrefix(command, "grep -q \"/mnt/nv/aftertouch.resolv.conf\"") {
|
||||
return "OK", nil
|
||||
}
|
||||
if strings.HasPrefix(command, "[ -f") {
|
||||
return "", fmt.Errorf("file not found")
|
||||
}
|
||||
@@ -1233,6 +1252,9 @@ func TestMigrateViaResolvConf_UdhcpcScript(t *testing.T) {
|
||||
if command == "cat /mnt/nv/rc.local" {
|
||||
return "#!/bin/sh\n", nil
|
||||
}
|
||||
if strings.HasPrefix(command, "grep -q \"/mnt/nv/aftertouch.resolv.conf\"") {
|
||||
return "OK", nil
|
||||
}
|
||||
if command == "[ -f "+targetScript+" ]" {
|
||||
return "", nil // file exists
|
||||
}
|
||||
@@ -1270,8 +1292,12 @@ func TestMigrateViaResolvConf_UdhcpcScript(t *testing.T) {
|
||||
if !strings.Contains(rcLocal, "targetScript=\"/opt/Bose/udhcpc.script\"") {
|
||||
t.Errorf("rc.local missing targetScript definition: %s", rcLocal)
|
||||
}
|
||||
if !strings.Contains(rcLocal, "sed -i '/echo \"search \\$search_list # \\$interface\" >> \\$RESOLV_CONF/a") {
|
||||
t.Errorf("rc.local missing sed patch for udhcpc.script: %s", rcLocal)
|
||||
if !strings.Contains(rcLocal, "sed -i '/echo \"search \\$search_list # \\$interface\" >> \\$RESOLV_CONF/a \\ [ -f '\"$HOOK_MARKER\"' ] && cat '\"$HOOK_MARKER\"' >> '\"\\$RESOLV_CONF\"' && dns=\"\"' \"$targetScript\"") {
|
||||
// Note: The actual string in rcLocal might have variables expanded or escaped depending on how it was constructed.
|
||||
// Let's check for the critical part: the escaped $RESOLV_CONF
|
||||
if !strings.Contains(rcLocal, ">> '\"\\$RESOLV_CONF\"'") {
|
||||
t.Errorf("rc.local missing correctly escaped RESOLV_CONF in sed patch for udhcpc.script: %s", rcLocal)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user