mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-07-18 04:07:00 +00:00
Fold runtime-key Docker URL metadata into the stable guest key
URLs saved through the resource drawer historically landed in the docker store under the runtime container key, which any stable record (including an intentionally empty cleared one) outranks in the unified customUrl projection, and which orphans on container recreation. On report ingest, copy those records into the stable app-container guest key when it is missing (cleared links stay cleared), healing saves stranded before the drawer moved to the stable identity. Also read the runtime key before its copy-if-missing container-name snapshot so the freshest write wins among the docker-store fallbacks. Refs #1556
This commit is contained in:
parent
7a1e41bcf9
commit
8f475cbf58
3 changed files with 177 additions and 3 deletions
|
|
@ -245,6 +245,43 @@ func (m *Monitor) migrateCurrentDockerContainerMetadataToStableIdentities(
|
|||
break
|
||||
}
|
||||
}
|
||||
|
||||
// URLs saved through the resource drawer historically landed in the
|
||||
// docker store under the runtime container key, which any stable
|
||||
// record outranks in the unified customUrl projection. Fold them
|
||||
// into the stable guest key so those saves surface in the tables
|
||||
// and survive container recreation. Runtime key first: it is where
|
||||
// writes landed, so it is fresher than its copy-if-missing snapshot
|
||||
// under the container-name key.
|
||||
if m.guestMetadataStore != nil && m.dockerMetadataStore != nil &&
|
||||
m.guestMetadataStore.Get(stableKey) == nil {
|
||||
source := m.dockerMetadataStore.Get(dockerContainerRuntimeMetadataKey(hostID, containerID))
|
||||
if source == nil {
|
||||
source = m.dockerMetadataStore.Get(dockerContainerNameMetadataKey(hostID, containerName))
|
||||
}
|
||||
if source != nil {
|
||||
clone := &config.GuestMetadata{
|
||||
CustomURL: source.CustomURL,
|
||||
Description: source.Description,
|
||||
LastKnownName: containerName,
|
||||
LastKnownType: "app-container",
|
||||
}
|
||||
if len(source.Tags) > 0 {
|
||||
clone.Tags = append([]string(nil), source.Tags...)
|
||||
}
|
||||
if len(source.Notes) > 0 {
|
||||
clone.Notes = append([]string(nil), source.Notes...)
|
||||
}
|
||||
if err := m.guestMetadataStore.Set(stableKey, clone); err != nil {
|
||||
log.Warn().
|
||||
Err(err).
|
||||
Str("dockerHostID", hostID).
|
||||
Str("containerName", container.Name).
|
||||
Str("containerID", container.ID).
|
||||
Msg("Failed to migrate docker metadata to stable app-container guest key")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5232,14 +5232,19 @@ func (m *Monitor) dockerAppContainerCustomURL(
|
|||
}
|
||||
|
||||
if m.dockerMetadataStore != nil {
|
||||
// The runtime key is where drawer saves used to land, so it is always at
|
||||
// least as fresh as the stable name key, which only receives
|
||||
// copy-if-missing snapshots of it. Prefer the runtime record while it
|
||||
// exists; the stable name key covers recreated containers whose old
|
||||
// runtime key no longer matches.
|
||||
if meta := m.dockerMetadataStore.Get(dockerContainerRuntimeMetadataKey(hostID, containerID)); meta != nil {
|
||||
return strings.TrimSpace(meta.CustomURL), true
|
||||
}
|
||||
if stableKey := dockerContainerNameMetadataKey(hostID, resource.Name); stableKey != "" {
|
||||
if meta := m.dockerMetadataStore.Get(stableKey); meta != nil {
|
||||
return strings.TrimSpace(meta.CustomURL), true
|
||||
}
|
||||
}
|
||||
if meta := m.dockerMetadataStore.Get(dockerContainerRuntimeMetadataKey(hostID, containerID)); meta != nil {
|
||||
return strings.TrimSpace(meta.CustomURL), true
|
||||
}
|
||||
}
|
||||
|
||||
return "", false
|
||||
|
|
|
|||
|
|
@ -508,6 +508,138 @@ func TestApplyDockerReportMigratesGuestMetadataToStableContainerName(t *testing.
|
|||
}
|
||||
}
|
||||
|
||||
func TestApplyDockerReportFoldsRuntimeKeyDockerMetadataIntoStableGuestKey(t *testing.T) {
|
||||
monitor := newTestMonitor(t)
|
||||
|
||||
baseTimestamp := time.Now().UTC()
|
||||
report := agentsdocker.Report{
|
||||
Agent: agentsdocker.AgentInfo{
|
||||
ID: "agent-runtime-fold",
|
||||
Version: "1.0.0",
|
||||
IntervalSeconds: 30,
|
||||
},
|
||||
Host: agentsdocker.HostInfo{
|
||||
Hostname: "docker-host-runtime-fold",
|
||||
MachineID: "machine-runtime-fold",
|
||||
},
|
||||
Containers: []agentsdocker.Container{
|
||||
{ID: "container-wud", Name: "/wud"},
|
||||
},
|
||||
Timestamp: baseTimestamp,
|
||||
}
|
||||
|
||||
host, err := monitor.ApplyDockerReport(report, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("first ApplyDockerReport failed: %v", err)
|
||||
}
|
||||
|
||||
// A URL saved through the resource drawer historically landed on the
|
||||
// runtime container key in the docker store.
|
||||
if err := monitor.dockerMetadataStore.Set(host.ID+":container:container-wud", &config.DockerMetadata{
|
||||
CustomURL: "https://wud.internal",
|
||||
}); err != nil {
|
||||
t.Fatalf("seed docker metadata: %v", err)
|
||||
}
|
||||
|
||||
report.Timestamp = baseTimestamp.Add(30 * time.Second)
|
||||
if _, err := monitor.ApplyDockerReport(report, nil); err != nil {
|
||||
t.Fatalf("second ApplyDockerReport failed: %v", err)
|
||||
}
|
||||
|
||||
stableKey := dockerAppContainerMetadataKey(host.ID, "wud")
|
||||
stableMeta := monitor.guestMetadataStore.Get(stableKey)
|
||||
if stableMeta == nil {
|
||||
t.Fatalf("expected runtime docker metadata folded into stable guest key %q", stableKey)
|
||||
}
|
||||
if stableMeta.CustomURL != "https://wud.internal" {
|
||||
t.Fatalf("stable guest metadata URL = %q, want https://wud.internal", stableMeta.CustomURL)
|
||||
}
|
||||
|
||||
resources := []unifiedresources.Resource{
|
||||
{
|
||||
ID: dockerAppContainerLegacyResourceID(host.ID, "container-wud"),
|
||||
Type: unifiedresources.ResourceTypeAppContainer,
|
||||
Name: "wud",
|
||||
Docker: &unifiedresources.DockerData{
|
||||
HostSourceID: host.ID,
|
||||
ContainerID: "container-wud",
|
||||
},
|
||||
},
|
||||
}
|
||||
got := monitor.applyDockerMetadataToUnifiedResources(resources)
|
||||
if got[0].CustomURL != "https://wud.internal" {
|
||||
t.Fatalf("projected CustomURL = %q, want https://wud.internal", got[0].CustomURL)
|
||||
}
|
||||
}
|
||||
|
||||
func TestApplyDockerReportKeepsClearedStableGuestKeyOverRuntimeDockerMetadata(t *testing.T) {
|
||||
monitor := newTestMonitor(t)
|
||||
|
||||
baseTimestamp := time.Now().UTC()
|
||||
report := agentsdocker.Report{
|
||||
Agent: agentsdocker.AgentInfo{
|
||||
ID: "agent-cleared-stable",
|
||||
Version: "1.0.0",
|
||||
IntervalSeconds: 30,
|
||||
},
|
||||
Host: agentsdocker.HostInfo{
|
||||
Hostname: "docker-host-cleared-stable",
|
||||
MachineID: "machine-cleared-stable",
|
||||
},
|
||||
Containers: []agentsdocker.Container{
|
||||
{ID: "container-cleared", Name: "cleared"},
|
||||
},
|
||||
Timestamp: baseTimestamp,
|
||||
}
|
||||
|
||||
host, err := monitor.ApplyDockerReport(report, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("first ApplyDockerReport failed: %v", err)
|
||||
}
|
||||
|
||||
// A user cleared the link: the stable guest key intentionally holds an
|
||||
// empty record. A stale runtime-key docker record must not overwrite or
|
||||
// outrank it.
|
||||
stableKey := dockerAppContainerMetadataKey(host.ID, "cleared")
|
||||
if err := monitor.guestMetadataStore.Set(stableKey, &config.GuestMetadata{
|
||||
CustomURL: "",
|
||||
LastKnownName: "cleared",
|
||||
LastKnownType: "app-container",
|
||||
}); err != nil {
|
||||
t.Fatalf("seed cleared guest metadata: %v", err)
|
||||
}
|
||||
if err := monitor.dockerMetadataStore.Set(host.ID+":container:container-cleared", &config.DockerMetadata{
|
||||
CustomURL: "https://stale.internal",
|
||||
}); err != nil {
|
||||
t.Fatalf("seed docker metadata: %v", err)
|
||||
}
|
||||
|
||||
report.Timestamp = baseTimestamp.Add(30 * time.Second)
|
||||
if _, err := monitor.ApplyDockerReport(report, nil); err != nil {
|
||||
t.Fatalf("second ApplyDockerReport failed: %v", err)
|
||||
}
|
||||
|
||||
if meta := monitor.guestMetadataStore.Get(stableKey); meta == nil || meta.CustomURL != "" {
|
||||
t.Fatalf("cleared stable guest metadata was overwritten: %#v", meta)
|
||||
}
|
||||
|
||||
resources := []unifiedresources.Resource{
|
||||
{
|
||||
ID: dockerAppContainerLegacyResourceID(host.ID, "container-cleared"),
|
||||
Type: unifiedresources.ResourceTypeAppContainer,
|
||||
Name: "cleared",
|
||||
Docker: &unifiedresources.DockerData{
|
||||
HostSourceID: host.ID,
|
||||
ContainerID: "container-cleared",
|
||||
},
|
||||
},
|
||||
}
|
||||
got := monitor.applyDockerMetadataToUnifiedResources(resources)
|
||||
if got[0].CustomURL != "" {
|
||||
t.Fatalf("projected CustomURL = %q, want empty (cleared)", got[0].CustomURL)
|
||||
}
|
||||
}
|
||||
|
||||
func TestApplyDockerReportSkipsMetadataMigrationForAmbiguousContainerNames(t *testing.T) {
|
||||
monitor := newTestMonitor(t)
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue