mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-04-28 11:30:15 +00:00
603 lines
19 KiB
Go
603 lines
19 KiB
Go
package alerts
|
|
|
|
import (
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/rcourtman/pulse-go-rewrite/internal/models"
|
|
)
|
|
|
|
// Helper to check if an alert exists with the given ID prefix
|
|
func hasAlertWithPrefix(alerts []Alert, prefix string) bool {
|
|
for _, a := range alerts {
|
|
if len(a.ID) >= len(prefix) && a.ID[:len(prefix)] == prefix {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func TestCheckDockerContainerImageUpdate(t *testing.T) {
|
|
// Create a manager for testing
|
|
m := NewManager()
|
|
|
|
// Set the default delay hours - NewManager doesn't set this,
|
|
// it's normally set during config loading
|
|
m.mu.Lock()
|
|
m.config.DockerDefaults.UpdateAlertDelayHours = 24 // Default: alert after 24 hours
|
|
m.mu.Unlock()
|
|
|
|
hostID := "docker-host-1"
|
|
containerID := "container-abc123"
|
|
resourceID := "docker:" + hostID + "/" + containerID
|
|
containerName := "test-container"
|
|
instanceName := "docker-instance"
|
|
nodeName := "docker-node"
|
|
|
|
host := models.DockerHost{ID: hostID, DisplayName: "Test Host"}
|
|
|
|
t.Run("no update status - no alert", func(t *testing.T) {
|
|
container := models.DockerContainer{
|
|
ID: containerID,
|
|
Name: containerName,
|
|
Image: "nginx:latest",
|
|
UpdateStatus: nil,
|
|
}
|
|
|
|
m.checkDockerContainerImageUpdate(host, container, resourceID, containerName, instanceName, nodeName)
|
|
|
|
alerts := m.GetActiveAlerts()
|
|
if hasAlertWithPrefix(alerts, "docker-container-update-"+resourceID) {
|
|
t.Error("Expected no alert when UpdateStatus is nil")
|
|
}
|
|
})
|
|
|
|
t.Run("update not available - no alert", func(t *testing.T) {
|
|
container := models.DockerContainer{
|
|
ID: containerID,
|
|
Name: containerName,
|
|
Image: "nginx:latest",
|
|
UpdateStatus: &models.DockerContainerUpdateStatus{
|
|
UpdateAvailable: false,
|
|
CurrentDigest: "sha256:current",
|
|
LatestDigest: "sha256:current",
|
|
LastChecked: time.Now(),
|
|
},
|
|
}
|
|
|
|
m.checkDockerContainerImageUpdate(host, container, resourceID, containerName, instanceName, nodeName)
|
|
|
|
alerts := m.GetActiveAlerts()
|
|
if hasAlertWithPrefix(alerts, "docker-container-update-"+resourceID) {
|
|
t.Error("Expected no alert when update is not available")
|
|
}
|
|
})
|
|
|
|
t.Run("update detection error - no alert", func(t *testing.T) {
|
|
container := models.DockerContainer{
|
|
ID: containerID,
|
|
Name: containerName,
|
|
Image: "nginx:latest",
|
|
UpdateStatus: &models.DockerContainerUpdateStatus{
|
|
UpdateAvailable: false,
|
|
Error: "rate limited",
|
|
LastChecked: time.Now(),
|
|
},
|
|
}
|
|
|
|
m.checkDockerContainerImageUpdate(host, container, resourceID, containerName, instanceName, nodeName)
|
|
|
|
alerts := m.GetActiveAlerts()
|
|
if hasAlertWithPrefix(alerts, "docker-container-update-"+resourceID) {
|
|
t.Error("Expected no alert when there's an error in update detection")
|
|
}
|
|
})
|
|
|
|
t.Run("update available but not yet past threshold - no alert", func(t *testing.T) {
|
|
container := models.DockerContainer{
|
|
ID: containerID,
|
|
Name: containerName,
|
|
Image: "nginx:latest",
|
|
UpdateStatus: &models.DockerContainerUpdateStatus{
|
|
UpdateAvailable: true,
|
|
CurrentDigest: "sha256:olddigest",
|
|
LatestDigest: "sha256:newdigest",
|
|
LastChecked: time.Now(),
|
|
},
|
|
}
|
|
|
|
// First call - should record firstSeen but not alert yet
|
|
m.checkDockerContainerImageUpdate(host, container, resourceID, containerName, instanceName, nodeName)
|
|
|
|
alerts := m.GetActiveAlerts()
|
|
if hasAlertWithPrefix(alerts, "docker-container-update-"+resourceID) {
|
|
t.Error("Expected no alert when update just detected (threshold not reached)")
|
|
}
|
|
|
|
// Verify tracking is set
|
|
m.mu.RLock()
|
|
_, tracked := m.dockerUpdateFirstSeen[resourceID]
|
|
trackingKey := dockerUpdateTrackingKey(host, container)
|
|
_, trackedByIdentity := m.dockerUpdateFirstSeenByIdentity[trackingKey]
|
|
m.mu.RUnlock()
|
|
if !tracked {
|
|
t.Error("Expected update to be tracked in dockerUpdateFirstSeen")
|
|
}
|
|
if !trackedByIdentity {
|
|
t.Error("Expected update to be tracked in dockerUpdateFirstSeenByIdentity")
|
|
}
|
|
})
|
|
|
|
t.Run("update available and past threshold - creates alert", func(t *testing.T) {
|
|
// Use a fresh resource ID for this test to avoid interference
|
|
testResourceID := "docker:" + hostID + "/container-threshold-test"
|
|
|
|
// Set up tracking as if we detected the update 25 hours ago
|
|
m.mu.Lock()
|
|
m.dockerUpdateFirstSeen[testResourceID] = time.Now().Add(-25 * time.Hour)
|
|
m.mu.Unlock()
|
|
|
|
container := models.DockerContainer{
|
|
ID: "container-threshold-test",
|
|
Name: "threshold-test-container",
|
|
Image: "nginx:latest",
|
|
UpdateStatus: &models.DockerContainerUpdateStatus{
|
|
UpdateAvailable: true,
|
|
CurrentDigest: "sha256:olddigest",
|
|
LatestDigest: "sha256:newdigest",
|
|
LastChecked: time.Now(),
|
|
},
|
|
}
|
|
|
|
m.checkDockerContainerImageUpdate(host, container, testResourceID, "threshold-test-container", instanceName, nodeName)
|
|
|
|
alerts := m.GetActiveAlerts()
|
|
found := false
|
|
for _, a := range alerts {
|
|
if a.Type == "docker-container-update" {
|
|
found = true
|
|
if a.Level != AlertLevelWarning {
|
|
t.Errorf("Expected level %q, got %q", AlertLevelWarning, a.Level)
|
|
}
|
|
break
|
|
}
|
|
}
|
|
if !found {
|
|
t.Error("Expected alert to be created after threshold")
|
|
}
|
|
})
|
|
|
|
t.Run("alert cleared when update applied", func(t *testing.T) {
|
|
// Use a fresh resource ID
|
|
testResourceID := "docker:" + hostID + "/container-clear-test"
|
|
|
|
// Set up an existing alert by triggering past threshold
|
|
m.mu.Lock()
|
|
m.dockerUpdateFirstSeen[testResourceID] = time.Now().Add(-25 * time.Hour)
|
|
m.mu.Unlock()
|
|
|
|
container := models.DockerContainer{
|
|
ID: "container-clear-test",
|
|
Name: "clear-test-container",
|
|
Image: "nginx:latest",
|
|
UpdateStatus: &models.DockerContainerUpdateStatus{
|
|
UpdateAvailable: true,
|
|
CurrentDigest: "sha256:olddigest",
|
|
LatestDigest: "sha256:newdigest",
|
|
LastChecked: time.Now(),
|
|
},
|
|
}
|
|
|
|
// First trigger the alert
|
|
m.checkDockerContainerImageUpdate(host, container, testResourceID, "clear-test-container", instanceName, nodeName)
|
|
|
|
// Now simulate container being updated
|
|
container.UpdateStatus = &models.DockerContainerUpdateStatus{
|
|
UpdateAvailable: false,
|
|
CurrentDigest: "sha256:newdigest",
|
|
LatestDigest: "sha256:newdigest",
|
|
LastChecked: time.Now(),
|
|
}
|
|
|
|
m.checkDockerContainerImageUpdate(host, container, testResourceID, "clear-test-container", instanceName, nodeName)
|
|
|
|
// Verify tracking is removed
|
|
m.mu.RLock()
|
|
_, tracked := m.dockerUpdateFirstSeen[testResourceID]
|
|
trackingKey := dockerUpdateTrackingKey(host, container)
|
|
_, trackedByIdentity := m.dockerUpdateFirstSeenByIdentity[trackingKey]
|
|
m.mu.RUnlock()
|
|
if tracked {
|
|
t.Error("Expected tracking to be removed when update is applied")
|
|
}
|
|
if trackedByIdentity {
|
|
t.Error("Expected identity tracking to be removed when update is applied")
|
|
}
|
|
})
|
|
|
|
t.Run("disabled by negative delay hours", func(t *testing.T) {
|
|
// Create manager with disabled update alerts
|
|
m2 := NewManager()
|
|
m2.mu.Lock()
|
|
m2.config.DockerDefaults.UpdateAlertDelayHours = -1 // Disable
|
|
m2.mu.Unlock()
|
|
|
|
testResourceID := "docker:" + hostID + "/container-disabled-test"
|
|
|
|
container := models.DockerContainer{
|
|
ID: "container-disabled-test",
|
|
Name: "disabled-test-container",
|
|
Image: "nginx:latest",
|
|
UpdateStatus: &models.DockerContainerUpdateStatus{
|
|
UpdateAvailable: true,
|
|
CurrentDigest: "sha256:olddigest",
|
|
LatestDigest: "sha256:newdigest",
|
|
LastChecked: time.Now(),
|
|
},
|
|
}
|
|
|
|
m2.checkDockerContainerImageUpdate(host, container, testResourceID, "disabled-test-container", instanceName, nodeName)
|
|
|
|
alerts := m2.GetActiveAlerts()
|
|
for _, a := range alerts {
|
|
if a.Type == "docker-container-update" {
|
|
t.Error("Expected no alert when delay hours is negative (disabled)")
|
|
break
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestCheckDockerContainerImageUpdatePreservesDelayAcrossHostIDChange(t *testing.T) {
|
|
m := NewManager()
|
|
m.mu.Lock()
|
|
m.config.DockerDefaults.UpdateAlertDelayHours = 24
|
|
m.mu.Unlock()
|
|
|
|
container := models.DockerContainer{
|
|
ID: "container-abc123",
|
|
Name: "web",
|
|
Image: "nginx:latest",
|
|
UpdateStatus: &models.DockerContainerUpdateStatus{
|
|
UpdateAvailable: true,
|
|
CurrentDigest: "sha256:old",
|
|
LatestDigest: "sha256:new",
|
|
LastChecked: time.Now(),
|
|
},
|
|
}
|
|
|
|
oldHost := models.DockerHost{
|
|
ID: "docker-host-old",
|
|
AgentID: "agent-stable-1",
|
|
DisplayName: "Old Docker Host",
|
|
Hostname: "docker.local",
|
|
}
|
|
newHost := oldHost
|
|
newHost.ID = "docker-host-new"
|
|
newHost.DisplayName = "New Docker Host"
|
|
|
|
oldResourceID := dockerResourceID(oldHost.ID, container.ID)
|
|
m.checkDockerContainerImageUpdate(oldHost, container, oldResourceID, "web", "docker-instance", "docker.local")
|
|
|
|
firstSeen := time.Now().Add(-25 * time.Hour)
|
|
trackingKey := dockerUpdateTrackingKey(oldHost, container)
|
|
m.mu.Lock()
|
|
m.dockerUpdateFirstSeen[oldResourceID] = firstSeen
|
|
m.dockerUpdateFirstSeenByIdentity[trackingKey] = firstSeen
|
|
m.mu.Unlock()
|
|
|
|
newResourceID := dockerResourceID(newHost.ID, container.ID)
|
|
m.checkDockerContainerImageUpdate(newHost, container, newResourceID, "web", "docker-instance", "docker.local")
|
|
|
|
alertID := "docker-container-update-" + newResourceID
|
|
m.mu.RLock()
|
|
alert, hasAlert := m.activeAlerts[alertID]
|
|
resourceFirstSeen, hasResourceTracking := m.dockerUpdateFirstSeen[newResourceID]
|
|
identityFirstSeen, hasIdentityTracking := m.dockerUpdateFirstSeenByIdentity[dockerUpdateTrackingKey(newHost, container)]
|
|
m.mu.RUnlock()
|
|
|
|
if !hasAlert {
|
|
t.Fatalf("Expected update alert %q after host ID change", alertID)
|
|
}
|
|
if !hasResourceTracking {
|
|
t.Fatalf("Expected resource tracking for new host resource ID")
|
|
}
|
|
if !hasIdentityTracking {
|
|
t.Fatalf("Expected identity tracking to persist across host ID change")
|
|
}
|
|
|
|
const tolerance = 2 * time.Second
|
|
if delta := alert.StartTime.Sub(firstSeen); delta < -tolerance || delta > tolerance {
|
|
t.Fatalf("Expected alert StartTime near %s, got %s", firstSeen, alert.StartTime)
|
|
}
|
|
if delta := resourceFirstSeen.Sub(firstSeen); delta < -tolerance || delta > tolerance {
|
|
t.Fatalf("Expected resource firstSeen near %s, got %s", firstSeen, resourceFirstSeen)
|
|
}
|
|
if delta := identityFirstSeen.Sub(firstSeen); delta < -tolerance || delta > tolerance {
|
|
t.Fatalf("Expected identity firstSeen near %s, got %s", firstSeen, identityFirstSeen)
|
|
}
|
|
}
|
|
|
|
// Note: Update alerts are now a free feature (no license gating).
|
|
// The Pro license gating tests were removed when update alerts became free.
|
|
|
|
func TestDockerUpdateTrackingCleanup(t *testing.T) {
|
|
m := NewManager()
|
|
|
|
hostID := "docker-host-1"
|
|
prefix := "docker:" + hostID + "/"
|
|
host := models.DockerHost{
|
|
ID: hostID,
|
|
AgentID: "agent-1",
|
|
Hostname: "docker-host-1.local",
|
|
}
|
|
otherHost := models.DockerHost{
|
|
ID: "other-host",
|
|
AgentID: "agent-other",
|
|
}
|
|
c1 := models.DockerContainer{ID: "container-1"}
|
|
c2 := models.DockerContainer{ID: "container-2"}
|
|
c3 := models.DockerContainer{ID: "container-3"}
|
|
otherContainer := models.DockerContainer{ID: "container-x"}
|
|
|
|
// Add some tracking entries
|
|
m.mu.Lock()
|
|
m.dockerUpdateFirstSeen[prefix+"container-1"] = time.Now()
|
|
m.dockerUpdateFirstSeen[prefix+"container-2"] = time.Now()
|
|
m.dockerUpdateFirstSeen[prefix+"container-3"] = time.Now()
|
|
m.dockerUpdateFirstSeen["docker:other-host/container-x"] = time.Now()
|
|
m.dockerUpdateFirstSeenByIdentity[dockerUpdateTrackingKey(host, c1)] = time.Now()
|
|
m.dockerUpdateFirstSeenByIdentity[dockerUpdateTrackingKey(host, c2)] = time.Now()
|
|
m.dockerUpdateFirstSeenByIdentity[dockerUpdateTrackingKey(host, c3)] = time.Now()
|
|
m.dockerUpdateFirstSeenByIdentity[dockerUpdateTrackingKey(otherHost, otherContainer)] = time.Now()
|
|
m.mu.Unlock()
|
|
|
|
// Create seen maps with only container-1 and container-2
|
|
seen := map[string]struct{}{
|
|
prefix + "container-1": {},
|
|
prefix + "container-2": {},
|
|
// container-3 is removed
|
|
}
|
|
seenUpdateTracking := map[string]struct{}{
|
|
dockerUpdateTrackingKey(host, c1): {},
|
|
dockerUpdateTrackingKey(host, c2): {},
|
|
}
|
|
|
|
// Run cleanup
|
|
m.cleanupDockerContainerAlertsWithTracking(host, seen, seenUpdateTracking)
|
|
|
|
// Verify container-3 tracking is removed
|
|
m.mu.RLock()
|
|
_, hasC1 := m.dockerUpdateFirstSeen[prefix+"container-1"]
|
|
_, hasC2 := m.dockerUpdateFirstSeen[prefix+"container-2"]
|
|
_, hasC3 := m.dockerUpdateFirstSeen[prefix+"container-3"]
|
|
_, hasOther := m.dockerUpdateFirstSeen["docker:other-host/container-x"]
|
|
_, hasC1ByIdentity := m.dockerUpdateFirstSeenByIdentity[dockerUpdateTrackingKey(host, c1)]
|
|
_, hasC2ByIdentity := m.dockerUpdateFirstSeenByIdentity[dockerUpdateTrackingKey(host, c2)]
|
|
_, hasC3ByIdentity := m.dockerUpdateFirstSeenByIdentity[dockerUpdateTrackingKey(host, c3)]
|
|
_, hasOtherByIdentity := m.dockerUpdateFirstSeenByIdentity[dockerUpdateTrackingKey(otherHost, otherContainer)]
|
|
m.mu.RUnlock()
|
|
|
|
if !hasC1 {
|
|
t.Error("Expected container-1 tracking to remain")
|
|
}
|
|
if !hasC2 {
|
|
t.Error("Expected container-2 tracking to remain")
|
|
}
|
|
if hasC3 {
|
|
t.Error("Expected container-3 tracking to be removed")
|
|
}
|
|
if !hasOther {
|
|
t.Error("Expected other host tracking to remain")
|
|
}
|
|
if !hasC1ByIdentity {
|
|
t.Error("Expected container-1 identity tracking to remain")
|
|
}
|
|
if !hasC2ByIdentity {
|
|
t.Error("Expected container-2 identity tracking to remain")
|
|
}
|
|
if hasC3ByIdentity {
|
|
t.Error("Expected container-3 identity tracking to be removed")
|
|
}
|
|
if !hasOtherByIdentity {
|
|
t.Error("Expected other host identity tracking to remain")
|
|
}
|
|
}
|
|
|
|
func TestUpdateConfigClearsDockerContainerUpdateAlertsWhenDisabled(t *testing.T) {
|
|
m := newTestManager(t)
|
|
|
|
host := models.DockerHost{
|
|
ID: "docker-host-1",
|
|
DisplayName: "Docker Host",
|
|
Hostname: "docker.local",
|
|
}
|
|
container := models.DockerContainer{
|
|
ID: "container-1",
|
|
Name: "/frontend",
|
|
Image: "nginx:latest",
|
|
}
|
|
resourceID := dockerResourceID(host.ID, container.ID)
|
|
alertID := "docker-container-update-" + resourceID
|
|
trackingKey := dockerUpdateTrackingKey(host, container)
|
|
firstSeen := time.Now().Add(-48 * time.Hour)
|
|
|
|
m.mu.Lock()
|
|
m.activeAlerts[alertID] = &Alert{
|
|
ID: alertID,
|
|
Type: "docker-container-update",
|
|
ResourceID: resourceID,
|
|
ResourceName: "frontend",
|
|
Instance: "Docker",
|
|
Metadata: map[string]interface{}{
|
|
"resourceType": "Docker Container",
|
|
"hostId": host.ID,
|
|
"hostName": host.DisplayName,
|
|
"hostHostname": host.Hostname,
|
|
"containerId": container.ID,
|
|
"containerName": "frontend",
|
|
"image": container.Image,
|
|
},
|
|
StartTime: firstSeen,
|
|
LastSeen: time.Now(),
|
|
}
|
|
m.dockerUpdateFirstSeen[resourceID] = firstSeen
|
|
m.dockerUpdateFirstSeenByIdentity[trackingKey] = firstSeen
|
|
m.mu.Unlock()
|
|
|
|
config := m.GetConfig()
|
|
config.DockerDefaults.UpdateAlertDelayHours = -1
|
|
m.UpdateConfig(config)
|
|
|
|
time.Sleep(50 * time.Millisecond)
|
|
|
|
m.mu.RLock()
|
|
_, hasAlert := m.activeAlerts[alertID]
|
|
_, hasResourceTracking := m.dockerUpdateFirstSeen[resourceID]
|
|
_, hasIdentityTracking := m.dockerUpdateFirstSeenByIdentity[trackingKey]
|
|
m.mu.RUnlock()
|
|
|
|
if hasAlert {
|
|
t.Fatalf("expected docker update alert to be cleared when update alerts are disabled")
|
|
}
|
|
if hasResourceTracking {
|
|
t.Fatalf("expected docker update resource tracking to be cleared when update alerts are disabled")
|
|
}
|
|
if hasIdentityTracking {
|
|
t.Fatalf("expected docker update identity tracking to be cleared when update alerts are disabled")
|
|
}
|
|
}
|
|
|
|
func TestUpdateConfigKeepsDockerContainerUpdateAlertsWhenStillEnabled(t *testing.T) {
|
|
m := newTestManager(t)
|
|
|
|
host := models.DockerHost{
|
|
ID: "docker-host-2",
|
|
DisplayName: "Docker Host",
|
|
Hostname: "docker-2.local",
|
|
}
|
|
container := models.DockerContainer{
|
|
ID: "container-2",
|
|
Name: "/api",
|
|
Image: "ghcr.io/example/api:latest",
|
|
}
|
|
resourceID := dockerResourceID(host.ID, container.ID)
|
|
alertID := "docker-container-update-" + resourceID
|
|
trackingKey := dockerUpdateTrackingKey(host, container)
|
|
firstSeen := time.Now().Add(-30 * time.Hour)
|
|
|
|
m.mu.Lock()
|
|
m.activeAlerts[alertID] = &Alert{
|
|
ID: alertID,
|
|
Type: "docker-container-update",
|
|
ResourceID: resourceID,
|
|
ResourceName: "api",
|
|
Instance: "Docker",
|
|
Metadata: map[string]interface{}{
|
|
"resourceType": "Docker Container",
|
|
"hostId": host.ID,
|
|
"hostName": host.DisplayName,
|
|
"hostHostname": host.Hostname,
|
|
"containerId": container.ID,
|
|
"containerName": "api",
|
|
"image": container.Image,
|
|
},
|
|
StartTime: firstSeen,
|
|
LastSeen: time.Now(),
|
|
}
|
|
m.dockerUpdateFirstSeen[resourceID] = firstSeen
|
|
m.dockerUpdateFirstSeenByIdentity[trackingKey] = firstSeen
|
|
m.mu.Unlock()
|
|
|
|
config := m.GetConfig()
|
|
config.DockerDefaults.CPU.Trigger = 95
|
|
config.DockerDefaults.CPU.Clear = 90
|
|
m.UpdateConfig(config)
|
|
|
|
time.Sleep(50 * time.Millisecond)
|
|
|
|
m.mu.RLock()
|
|
_, hasAlert := m.activeAlerts[alertID]
|
|
resourceFirstSeen, hasResourceTracking := m.dockerUpdateFirstSeen[resourceID]
|
|
identityFirstSeen, hasIdentityTracking := m.dockerUpdateFirstSeenByIdentity[trackingKey]
|
|
m.mu.RUnlock()
|
|
|
|
if !hasAlert {
|
|
t.Fatalf("expected docker update alert to remain active when update alerts are still enabled")
|
|
}
|
|
if !hasResourceTracking {
|
|
t.Fatalf("expected docker update resource tracking to remain when update alerts stay enabled")
|
|
}
|
|
if !hasIdentityTracking {
|
|
t.Fatalf("expected docker update identity tracking to remain when update alerts stay enabled")
|
|
}
|
|
if !resourceFirstSeen.Equal(firstSeen) {
|
|
t.Fatalf("expected docker update resource tracking to preserve firstSeen, got %s want %s", resourceFirstSeen, firstSeen)
|
|
}
|
|
if !identityFirstSeen.Equal(firstSeen) {
|
|
t.Fatalf("expected docker update identity tracking to preserve firstSeen, got %s want %s", identityFirstSeen, firstSeen)
|
|
}
|
|
}
|
|
|
|
func TestEvaluateDockerContainerClearsUpdateAlertWhenOverrideDisabled(t *testing.T) {
|
|
m := newTestManager(t)
|
|
|
|
host := models.DockerHost{
|
|
ID: "docker-host-3",
|
|
DisplayName: "Docker Host",
|
|
Hostname: "docker-3.local",
|
|
}
|
|
container := models.DockerContainer{
|
|
ID: "container-3",
|
|
Name: "/worker",
|
|
Image: "ghcr.io/example/worker:latest",
|
|
}
|
|
resourceID := dockerResourceID(host.ID, container.ID)
|
|
alertID := "docker-container-update-" + resourceID
|
|
trackingKey := dockerUpdateTrackingKey(host, container)
|
|
firstSeen := time.Now().Add(-36 * time.Hour)
|
|
|
|
m.mu.Lock()
|
|
m.activeAlerts[alertID] = &Alert{
|
|
ID: alertID,
|
|
Type: "docker-container-update",
|
|
ResourceID: resourceID,
|
|
ResourceName: "worker",
|
|
Instance: "Docker",
|
|
Metadata: map[string]interface{}{
|
|
"resourceType": "Docker Container",
|
|
"hostId": host.ID,
|
|
"hostName": host.DisplayName,
|
|
"hostHostname": host.Hostname,
|
|
"containerId": container.ID,
|
|
"containerName": "worker",
|
|
"image": container.Image,
|
|
},
|
|
StartTime: firstSeen,
|
|
LastSeen: time.Now(),
|
|
}
|
|
m.dockerUpdateFirstSeen[resourceID] = firstSeen
|
|
m.dockerUpdateFirstSeenByIdentity[trackingKey] = firstSeen
|
|
m.config.Overrides[resourceID] = ThresholdConfig{Disabled: true}
|
|
m.mu.Unlock()
|
|
|
|
m.evaluateDockerContainer(host, container, resourceID)
|
|
|
|
time.Sleep(50 * time.Millisecond)
|
|
|
|
m.mu.RLock()
|
|
_, hasAlert := m.activeAlerts[alertID]
|
|
_, hasResourceTracking := m.dockerUpdateFirstSeen[resourceID]
|
|
_, hasIdentityTracking := m.dockerUpdateFirstSeenByIdentity[trackingKey]
|
|
m.mu.RUnlock()
|
|
|
|
if hasAlert {
|
|
t.Fatalf("expected docker update alert to be cleared when the container override disables alerts")
|
|
}
|
|
if hasResourceTracking {
|
|
t.Fatalf("expected docker update resource tracking to be cleared when the container override disables alerts")
|
|
}
|
|
if hasIdentityTracking {
|
|
t.Fatalf("expected docker update identity tracking to be cleared when the container override disables alerts")
|
|
}
|
|
}
|