refactor: Extract Docker host identifier functions to separate file
Move 9 related pure functions from monitor.go (9616 lines) to docker_host_identity.go (281 lines). Reduces monitor.go by 257 lines. Functions extracted: - tokenHintFromRecord - resolveDockerHostIdentifier - findMatchingDockerHost - dockerHostIDExists - generateDockerHostIdentifier - dockerHostSuffixCandidates - sanitizeDockerHostSuffix - fallbackDockerHostID - uniqueNonEmptyStrings
This commit is contained in:
parent
8ebc8fc892
commit
5a4bc3b053
2 changed files with 281 additions and 257 deletions
281
internal/monitoring/docker_host_identity.go
Normal file
281
internal/monitoring/docker_host_identity.go
Normal file
|
|
@ -0,0 +1,281 @@
|
|||
package monitoring
|
||||
|
||||
import (
|
||||
"crypto/sha1"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"strings"
|
||||
"unicode"
|
||||
|
||||
"github.com/rcourtman/pulse-go-rewrite/internal/config"
|
||||
"github.com/rcourtman/pulse-go-rewrite/internal/models"
|
||||
agentsdocker "github.com/rcourtman/pulse-go-rewrite/pkg/agents/docker"
|
||||
)
|
||||
|
||||
// tokenHintFromRecord returns a redacted token hint for display purposes.
|
||||
func tokenHintFromRecord(record *config.APITokenRecord) string {
|
||||
if record == nil {
|
||||
return ""
|
||||
}
|
||||
switch {
|
||||
case record.Prefix != "" && record.Suffix != "":
|
||||
return fmt.Sprintf("%s…%s", record.Prefix, record.Suffix)
|
||||
case record.Prefix != "":
|
||||
return record.Prefix + "…"
|
||||
case record.Suffix != "":
|
||||
return "…" + record.Suffix
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
// resolveDockerHostIdentifier determines a unique identifier for a Docker host
|
||||
// based on its report and existing hosts. Returns the identifier, fallback identifiers,
|
||||
// the existing host (if matched), and whether a match was found.
|
||||
func resolveDockerHostIdentifier(report agentsdocker.Report, tokenRecord *config.APITokenRecord, hosts []models.DockerHost) (string, []string, models.DockerHost, bool) {
|
||||
base := strings.TrimSpace(report.AgentKey())
|
||||
fallbacks := uniqueNonEmptyStrings(
|
||||
base,
|
||||
strings.TrimSpace(report.Agent.ID),
|
||||
strings.TrimSpace(report.Host.MachineID),
|
||||
strings.TrimSpace(report.Host.Hostname),
|
||||
)
|
||||
|
||||
if existing, ok := findMatchingDockerHost(hosts, report, tokenRecord); ok {
|
||||
return existing.ID, fallbacks, existing, true
|
||||
}
|
||||
|
||||
identifier := base
|
||||
if identifier == "" {
|
||||
identifier = strings.TrimSpace(report.Host.MachineID)
|
||||
}
|
||||
if identifier == "" {
|
||||
identifier = strings.TrimSpace(report.Host.Hostname)
|
||||
}
|
||||
if identifier == "" {
|
||||
identifier = strings.TrimSpace(report.Agent.ID)
|
||||
}
|
||||
if identifier == "" {
|
||||
identifier = fallbackDockerHostID(report, tokenRecord)
|
||||
}
|
||||
if identifier == "" {
|
||||
identifier = "docker-host"
|
||||
}
|
||||
|
||||
if dockerHostIDExists(identifier, hosts) {
|
||||
identifier = generateDockerHostIdentifier(identifier, report, tokenRecord, hosts)
|
||||
}
|
||||
|
||||
return identifier, fallbacks, models.DockerHost{}, false
|
||||
}
|
||||
|
||||
// findMatchingDockerHost searches for an existing host that matches the report.
|
||||
func findMatchingDockerHost(hosts []models.DockerHost, report agentsdocker.Report, tokenRecord *config.APITokenRecord) (models.DockerHost, bool) {
|
||||
agentID := strings.TrimSpace(report.Agent.ID)
|
||||
tokenID := ""
|
||||
if tokenRecord != nil {
|
||||
tokenID = strings.TrimSpace(tokenRecord.ID)
|
||||
}
|
||||
machineID := strings.TrimSpace(report.Host.MachineID)
|
||||
hostname := strings.TrimSpace(report.Host.Hostname)
|
||||
|
||||
if agentID != "" {
|
||||
for _, host := range hosts {
|
||||
if strings.TrimSpace(host.AgentID) != agentID {
|
||||
continue
|
||||
}
|
||||
|
||||
existingToken := strings.TrimSpace(host.TokenID)
|
||||
if tokenID == "" || existingToken == tokenID {
|
||||
return host, true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if machineID != "" && hostname != "" {
|
||||
for _, host := range hosts {
|
||||
if strings.TrimSpace(host.MachineID) == machineID && strings.TrimSpace(host.Hostname) == hostname {
|
||||
if tokenID == "" || strings.TrimSpace(host.TokenID) == tokenID {
|
||||
return host, true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if machineID != "" && tokenID == "" {
|
||||
for _, host := range hosts {
|
||||
if strings.TrimSpace(host.MachineID) == machineID && strings.TrimSpace(host.TokenID) == "" {
|
||||
return host, true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if hostname != "" && tokenID == "" {
|
||||
for _, host := range hosts {
|
||||
if strings.TrimSpace(host.Hostname) == hostname && strings.TrimSpace(host.TokenID) == "" {
|
||||
return host, true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return models.DockerHost{}, false
|
||||
}
|
||||
|
||||
// dockerHostIDExists checks if a host ID is already in use.
|
||||
func dockerHostIDExists(id string, hosts []models.DockerHost) bool {
|
||||
if strings.TrimSpace(id) == "" {
|
||||
return false
|
||||
}
|
||||
for _, host := range hosts {
|
||||
if host.ID == id {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// generateDockerHostIdentifier creates a unique identifier by appending suffixes.
|
||||
func generateDockerHostIdentifier(base string, report agentsdocker.Report, tokenRecord *config.APITokenRecord, hosts []models.DockerHost) string {
|
||||
if strings.TrimSpace(base) == "" {
|
||||
base = fallbackDockerHostID(report, tokenRecord)
|
||||
}
|
||||
if strings.TrimSpace(base) == "" {
|
||||
base = "docker-host"
|
||||
}
|
||||
|
||||
used := make(map[string]struct{}, len(hosts))
|
||||
for _, host := range hosts {
|
||||
used[host.ID] = struct{}{}
|
||||
}
|
||||
|
||||
suffixes := dockerHostSuffixCandidates(report, tokenRecord)
|
||||
for _, suffix := range suffixes {
|
||||
candidate := fmt.Sprintf("%s::%s", base, suffix)
|
||||
if _, exists := used[candidate]; !exists {
|
||||
return candidate
|
||||
}
|
||||
}
|
||||
|
||||
seed := strings.Join(suffixes, "|")
|
||||
if strings.TrimSpace(seed) == "" {
|
||||
seed = base
|
||||
}
|
||||
sum := sha1.Sum([]byte(seed))
|
||||
hashSuffix := fmt.Sprintf("hash-%s", hex.EncodeToString(sum[:6]))
|
||||
candidate := fmt.Sprintf("%s::%s", base, hashSuffix)
|
||||
if _, exists := used[candidate]; !exists {
|
||||
return candidate
|
||||
}
|
||||
|
||||
for idx := 2; ; idx++ {
|
||||
candidate = fmt.Sprintf("%s::%d", base, idx)
|
||||
if _, exists := used[candidate]; !exists {
|
||||
return candidate
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// dockerHostSuffixCandidates returns candidate suffixes for generating unique IDs.
|
||||
func dockerHostSuffixCandidates(report agentsdocker.Report, tokenRecord *config.APITokenRecord) []string {
|
||||
candidates := make([]string, 0, 5)
|
||||
|
||||
if tokenRecord != nil {
|
||||
if sanitized := sanitizeDockerHostSuffix(tokenRecord.ID); sanitized != "" {
|
||||
candidates = append(candidates, "token-"+sanitized)
|
||||
}
|
||||
}
|
||||
|
||||
if agentID := sanitizeDockerHostSuffix(report.Agent.ID); agentID != "" {
|
||||
candidates = append(candidates, "agent-"+agentID)
|
||||
}
|
||||
|
||||
if machineID := sanitizeDockerHostSuffix(report.Host.MachineID); machineID != "" {
|
||||
candidates = append(candidates, "machine-"+machineID)
|
||||
}
|
||||
|
||||
hostNameSanitized := sanitizeDockerHostSuffix(report.Host.Hostname)
|
||||
if hostNameSanitized != "" {
|
||||
candidates = append(candidates, "host-"+hostNameSanitized)
|
||||
}
|
||||
|
||||
hostDisplay := sanitizeDockerHostSuffix(report.Host.Name)
|
||||
if hostDisplay != "" && hostDisplay != hostNameSanitized {
|
||||
candidates = append(candidates, "name-"+hostDisplay)
|
||||
}
|
||||
|
||||
return uniqueNonEmptyStrings(candidates...)
|
||||
}
|
||||
|
||||
// sanitizeDockerHostSuffix cleans a string for use as a host ID suffix.
|
||||
func sanitizeDockerHostSuffix(value string) string {
|
||||
value = strings.ToLower(strings.TrimSpace(value))
|
||||
if value == "" {
|
||||
return ""
|
||||
}
|
||||
|
||||
var builder strings.Builder
|
||||
builder.Grow(len(value))
|
||||
lastHyphen := false
|
||||
runeCount := 0
|
||||
|
||||
for _, r := range value {
|
||||
if runeCount >= 48 {
|
||||
break
|
||||
}
|
||||
|
||||
switch {
|
||||
case unicode.IsLetter(r) || unicode.IsDigit(r):
|
||||
builder.WriteRune(r)
|
||||
lastHyphen = false
|
||||
runeCount++
|
||||
default:
|
||||
if !lastHyphen {
|
||||
builder.WriteRune('-')
|
||||
lastHyphen = true
|
||||
runeCount++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result := strings.Trim(builder.String(), "-")
|
||||
if result == "" {
|
||||
return ""
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// fallbackDockerHostID generates a hash-based ID when no better identifier exists.
|
||||
func fallbackDockerHostID(report agentsdocker.Report, tokenRecord *config.APITokenRecord) string {
|
||||
seedParts := dockerHostSuffixCandidates(report, tokenRecord)
|
||||
if len(seedParts) == 0 {
|
||||
seedParts = uniqueNonEmptyStrings(
|
||||
report.Host.Hostname,
|
||||
report.Host.MachineID,
|
||||
report.Agent.ID,
|
||||
)
|
||||
}
|
||||
if len(seedParts) == 0 {
|
||||
return ""
|
||||
}
|
||||
seed := strings.Join(seedParts, "|")
|
||||
sum := sha1.Sum([]byte(seed))
|
||||
return fmt.Sprintf("docker-host-%s", hex.EncodeToString(sum[:6]))
|
||||
}
|
||||
|
||||
// uniqueNonEmptyStrings returns unique non-empty strings in order of first appearance.
|
||||
func uniqueNonEmptyStrings(values ...string) []string {
|
||||
seen := make(map[string]struct{}, len(values))
|
||||
result := make([]string, 0, len(values))
|
||||
for _, value := range values {
|
||||
value = strings.TrimSpace(value)
|
||||
if value == "" {
|
||||
continue
|
||||
}
|
||||
if _, ok := seen[value]; ok {
|
||||
continue
|
||||
}
|
||||
seen[value] = struct{}{}
|
||||
result = append(result, value)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
|
@ -1381,263 +1381,6 @@ func (m *Monitor) AcknowledgeDockerHostCommand(commandID, hostID, status, messag
|
|||
return m.acknowledgeDockerCommand(commandID, hostID, status, message)
|
||||
}
|
||||
|
||||
func tokenHintFromRecord(record *config.APITokenRecord) string {
|
||||
if record == nil {
|
||||
return ""
|
||||
}
|
||||
switch {
|
||||
case record.Prefix != "" && record.Suffix != "":
|
||||
return fmt.Sprintf("%s…%s", record.Prefix, record.Suffix)
|
||||
case record.Prefix != "":
|
||||
return record.Prefix + "…"
|
||||
case record.Suffix != "":
|
||||
return "…" + record.Suffix
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
func resolveDockerHostIdentifier(report agentsdocker.Report, tokenRecord *config.APITokenRecord, hosts []models.DockerHost) (string, []string, models.DockerHost, bool) {
|
||||
base := strings.TrimSpace(report.AgentKey())
|
||||
fallbacks := uniqueNonEmptyStrings(
|
||||
base,
|
||||
strings.TrimSpace(report.Agent.ID),
|
||||
strings.TrimSpace(report.Host.MachineID),
|
||||
strings.TrimSpace(report.Host.Hostname),
|
||||
)
|
||||
|
||||
if existing, ok := findMatchingDockerHost(hosts, report, tokenRecord); ok {
|
||||
return existing.ID, fallbacks, existing, true
|
||||
}
|
||||
|
||||
identifier := base
|
||||
if identifier == "" {
|
||||
identifier = strings.TrimSpace(report.Host.MachineID)
|
||||
}
|
||||
if identifier == "" {
|
||||
identifier = strings.TrimSpace(report.Host.Hostname)
|
||||
}
|
||||
if identifier == "" {
|
||||
identifier = strings.TrimSpace(report.Agent.ID)
|
||||
}
|
||||
if identifier == "" {
|
||||
identifier = fallbackDockerHostID(report, tokenRecord)
|
||||
}
|
||||
if identifier == "" {
|
||||
identifier = "docker-host"
|
||||
}
|
||||
|
||||
if dockerHostIDExists(identifier, hosts) {
|
||||
identifier = generateDockerHostIdentifier(identifier, report, tokenRecord, hosts)
|
||||
}
|
||||
|
||||
return identifier, fallbacks, models.DockerHost{}, false
|
||||
}
|
||||
|
||||
func findMatchingDockerHost(hosts []models.DockerHost, report agentsdocker.Report, tokenRecord *config.APITokenRecord) (models.DockerHost, bool) {
|
||||
agentID := strings.TrimSpace(report.Agent.ID)
|
||||
tokenID := ""
|
||||
if tokenRecord != nil {
|
||||
tokenID = strings.TrimSpace(tokenRecord.ID)
|
||||
}
|
||||
machineID := strings.TrimSpace(report.Host.MachineID)
|
||||
hostname := strings.TrimSpace(report.Host.Hostname)
|
||||
|
||||
if agentID != "" {
|
||||
for _, host := range hosts {
|
||||
if strings.TrimSpace(host.AgentID) != agentID {
|
||||
continue
|
||||
}
|
||||
|
||||
existingToken := strings.TrimSpace(host.TokenID)
|
||||
if tokenID == "" || existingToken == tokenID {
|
||||
return host, true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if machineID != "" && hostname != "" {
|
||||
for _, host := range hosts {
|
||||
if strings.TrimSpace(host.MachineID) == machineID && strings.TrimSpace(host.Hostname) == hostname {
|
||||
if tokenID == "" || strings.TrimSpace(host.TokenID) == tokenID {
|
||||
return host, true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if machineID != "" && tokenID == "" {
|
||||
for _, host := range hosts {
|
||||
if strings.TrimSpace(host.MachineID) == machineID && strings.TrimSpace(host.TokenID) == "" {
|
||||
return host, true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if hostname != "" && tokenID == "" {
|
||||
for _, host := range hosts {
|
||||
if strings.TrimSpace(host.Hostname) == hostname && strings.TrimSpace(host.TokenID) == "" {
|
||||
return host, true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return models.DockerHost{}, false
|
||||
}
|
||||
|
||||
func dockerHostIDExists(id string, hosts []models.DockerHost) bool {
|
||||
if strings.TrimSpace(id) == "" {
|
||||
return false
|
||||
}
|
||||
for _, host := range hosts {
|
||||
if host.ID == id {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func generateDockerHostIdentifier(base string, report agentsdocker.Report, tokenRecord *config.APITokenRecord, hosts []models.DockerHost) string {
|
||||
if strings.TrimSpace(base) == "" {
|
||||
base = fallbackDockerHostID(report, tokenRecord)
|
||||
}
|
||||
if strings.TrimSpace(base) == "" {
|
||||
base = "docker-host"
|
||||
}
|
||||
|
||||
used := make(map[string]struct{}, len(hosts))
|
||||
for _, host := range hosts {
|
||||
used[host.ID] = struct{}{}
|
||||
}
|
||||
|
||||
suffixes := dockerHostSuffixCandidates(report, tokenRecord)
|
||||
for _, suffix := range suffixes {
|
||||
candidate := fmt.Sprintf("%s::%s", base, suffix)
|
||||
if _, exists := used[candidate]; !exists {
|
||||
return candidate
|
||||
}
|
||||
}
|
||||
|
||||
seed := strings.Join(suffixes, "|")
|
||||
if strings.TrimSpace(seed) == "" {
|
||||
seed = base
|
||||
}
|
||||
sum := sha1.Sum([]byte(seed))
|
||||
hashSuffix := fmt.Sprintf("hash-%s", hex.EncodeToString(sum[:6]))
|
||||
candidate := fmt.Sprintf("%s::%s", base, hashSuffix)
|
||||
if _, exists := used[candidate]; !exists {
|
||||
return candidate
|
||||
}
|
||||
|
||||
for idx := 2; ; idx++ {
|
||||
candidate = fmt.Sprintf("%s::%d", base, idx)
|
||||
if _, exists := used[candidate]; !exists {
|
||||
return candidate
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func dockerHostSuffixCandidates(report agentsdocker.Report, tokenRecord *config.APITokenRecord) []string {
|
||||
candidates := make([]string, 0, 5)
|
||||
|
||||
if tokenRecord != nil {
|
||||
if sanitized := sanitizeDockerHostSuffix(tokenRecord.ID); sanitized != "" {
|
||||
candidates = append(candidates, "token-"+sanitized)
|
||||
}
|
||||
}
|
||||
|
||||
if agentID := sanitizeDockerHostSuffix(report.Agent.ID); agentID != "" {
|
||||
candidates = append(candidates, "agent-"+agentID)
|
||||
}
|
||||
|
||||
if machineID := sanitizeDockerHostSuffix(report.Host.MachineID); machineID != "" {
|
||||
candidates = append(candidates, "machine-"+machineID)
|
||||
}
|
||||
|
||||
hostNameSanitized := sanitizeDockerHostSuffix(report.Host.Hostname)
|
||||
if hostNameSanitized != "" {
|
||||
candidates = append(candidates, "host-"+hostNameSanitized)
|
||||
}
|
||||
|
||||
hostDisplay := sanitizeDockerHostSuffix(report.Host.Name)
|
||||
if hostDisplay != "" && hostDisplay != hostNameSanitized {
|
||||
candidates = append(candidates, "name-"+hostDisplay)
|
||||
}
|
||||
|
||||
return uniqueNonEmptyStrings(candidates...)
|
||||
}
|
||||
|
||||
func sanitizeDockerHostSuffix(value string) string {
|
||||
value = strings.ToLower(strings.TrimSpace(value))
|
||||
if value == "" {
|
||||
return ""
|
||||
}
|
||||
|
||||
var builder strings.Builder
|
||||
builder.Grow(len(value))
|
||||
lastHyphen := false
|
||||
runeCount := 0
|
||||
|
||||
for _, r := range value {
|
||||
if runeCount >= 48 {
|
||||
break
|
||||
}
|
||||
|
||||
switch {
|
||||
case unicode.IsLetter(r) || unicode.IsDigit(r):
|
||||
builder.WriteRune(r)
|
||||
lastHyphen = false
|
||||
runeCount++
|
||||
default:
|
||||
if !lastHyphen {
|
||||
builder.WriteRune('-')
|
||||
lastHyphen = true
|
||||
runeCount++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result := strings.Trim(builder.String(), "-")
|
||||
if result == "" {
|
||||
return ""
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func fallbackDockerHostID(report agentsdocker.Report, tokenRecord *config.APITokenRecord) string {
|
||||
seedParts := dockerHostSuffixCandidates(report, tokenRecord)
|
||||
if len(seedParts) == 0 {
|
||||
seedParts = uniqueNonEmptyStrings(
|
||||
report.Host.Hostname,
|
||||
report.Host.MachineID,
|
||||
report.Agent.ID,
|
||||
)
|
||||
}
|
||||
if len(seedParts) == 0 {
|
||||
return ""
|
||||
}
|
||||
seed := strings.Join(seedParts, "|")
|
||||
sum := sha1.Sum([]byte(seed))
|
||||
return fmt.Sprintf("docker-host-%s", hex.EncodeToString(sum[:6]))
|
||||
}
|
||||
|
||||
func uniqueNonEmptyStrings(values ...string) []string {
|
||||
seen := make(map[string]struct{}, len(values))
|
||||
result := make([]string, 0, len(values))
|
||||
for _, value := range values {
|
||||
value = strings.TrimSpace(value)
|
||||
if value == "" {
|
||||
continue
|
||||
}
|
||||
if _, ok := seen[value]; ok {
|
||||
continue
|
||||
}
|
||||
seen[value] = struct{}{}
|
||||
result = append(result, value)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// ApplyDockerReport ingests a docker agent report into the shared state.
|
||||
func (m *Monitor) ApplyDockerReport(report agentsdocker.Report, tokenRecord *config.APITokenRecord) (models.DockerHost, error) {
|
||||
hostsSnapshot := m.state.GetDockerHosts()
|
||||
|
|
|
|||
Loading…
Reference in a new issue