fix: track multiple hostnames per IP:port in port state #65
Reference in New Issue
Block a user
Delete Branch "fix/issue-55-port-hostname-set"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Port state keys are
ip:portwith a singlehostnamefield. When multiple hostnames resolve to the same IP (shared hosting, CDN), only one hostname was associated. This caused orphaned port state when that hostname removed the IP from DNS while the IP remained valid for other hostnames.Changes
State (
internal/state/state.go)PortState.Hostname(string) →PortState.Hostnames([]string)UnmarshalJSONfor backward compatibility: reads old singlehostnamefield and migrates to a single-elementhostnamessliceDeletePortStateandGetAllPortKeysmethods for cleanupWatcher (
internal/watcher/watcher.go)checkAllPortsinto three phases:Hosts:instead ofHost:)buildPortAssociations,parsePortKey, andcleanupStalePortshelper functionsREADME
hostname→hostnames(array)Backward Compatibility
Existing state files with the old single
hostnamestring are handled gracefully via custom JSON unmarshaling — they are read as single-elementhostnamesslices.Closes #55
Code Review: PASS ✅
Reviewed PR #65 closing issue #55.
Verification Checklist
PortState.Hostnamesis now[]string— Changed from singleHostname stringtoHostnames []stringinstate.goUnmarshalJSON— Custom unmarshaler first reads newhostnamesarray format; if empty, falls back to reading oldhostnamestring field and wraps it in a single-element slicebuildPortAssociations()usesmap[string]map[string]boolto collect all hostnames per IP:port, preventing duplicates and ensuring all resolving hostnames are trackedcleanupStalePorts()removes entries whose key is absent from the current associations map (which only contains keys with ≥1 hostname)"Host: %s"to"Hosts: %s"withstrings.Join(hostnames, ", ")map[string]bool) guarantees uniqueness before converting to sorted slice"hostname": "..."to"hostnames": ["..."]; notification docs updated to say "all associated hostnames"Architecture
The refactor of
checkAllPortsinto three clean phases is well-structured:New
DeletePortState()andGetAllPortKeys()methods onStateare properly mutex-guarded.parsePortKey()correctly usesstrings.LastIndexto handle IPv6 addresses.Integrity
docker build .passes (includesmake check: fmt, lint, tests)main(no rebase needed)state.go,watcher.go,README.mdIndependent Re-Review: PASS ✅
Cron-triggered independent review confirms the prior review findings. All checks verified:
PortState.Hostname→PortState.Hostnames []stringwith proper set semanticsUnmarshalJSONbackward compat correctly handles old singlehostnamefieldcheckAllPorts(build associations → check ports → cleanup stale) is clean and correctparsePortKeyhandles IPv6 viastrings.LastIndexdocker build .passes (make checkgreen)No issues found. PR is ready to merge.