mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-09-10 10:35:51 +00:00
dc6b5c3197
Two AI write paths asserted unverified success while the Proxmox guest and Docker start/stop/restart handlers already do read-after-write checks. Bring both in line with that idiom: - pulse_kubernetes scale: re-read the deployment's spec/ready replicas via kubectl through the same agent (bounded settle-and-retry window) and return a JSON response with a verification block instead of "Action complete - no verification needed". - pulse_docker update: the docker agent already recreates the container, health-checks it, rolls back on failure, and acks a terminal command status; expose that status through a new Monitor lookup (GetDockerCommandStatus) plus UpdatesProvider.GetCommandStatus, and poll it within a bounded window. Responses now report verified success, verified failure (is_error), or an explicit inconclusive, never unverified success. kubernetes_control_test.go also carries a small in-flight fix from the parallel remediation-lock work (in-memory ActionAuditStore in the test helper) that these tests require to run on this tree.
175 lines
4.3 KiB
Go
175 lines
4.3 KiB
Go
package tools
|
|
|
|
import (
|
|
"context"
|
|
"time"
|
|
|
|
"github.com/rcourtman/pulse-go-rewrite/internal/agentexec"
|
|
"github.com/rcourtman/pulse-go-rewrite/internal/models"
|
|
"github.com/rcourtman/pulse-go-rewrite/internal/unifiedresources"
|
|
"github.com/stretchr/testify/mock"
|
|
)
|
|
|
|
// Mock implementations for testing
|
|
|
|
type mockStateProvider struct {
|
|
mock.Mock
|
|
state models.StateSnapshot
|
|
}
|
|
|
|
func (m *mockStateProvider) ReadSnapshot() models.StateSnapshot {
|
|
if len(m.ExpectedCalls) == 0 {
|
|
return m.state
|
|
}
|
|
args := m.Called()
|
|
if args.Get(0) == nil {
|
|
return models.StateSnapshot{}
|
|
}
|
|
return args.Get(0).(models.StateSnapshot)
|
|
}
|
|
|
|
type mockCommandPolicy struct {
|
|
mock.Mock
|
|
}
|
|
|
|
func (m *mockCommandPolicy) Evaluate(command string) agentexec.PolicyDecision {
|
|
args := m.Called(command)
|
|
return args.Get(0).(agentexec.PolicyDecision)
|
|
}
|
|
|
|
type mockAgentServer struct {
|
|
mock.Mock
|
|
agents []agentexec.ConnectedAgent
|
|
}
|
|
|
|
func (m *mockAgentServer) GetConnectedAgents() []agentexec.ConnectedAgent {
|
|
if len(m.ExpectedCalls) == 0 {
|
|
return m.agents
|
|
}
|
|
args := m.Called()
|
|
if args.Get(0) == nil {
|
|
return nil
|
|
}
|
|
return args.Get(0).([]agentexec.ConnectedAgent)
|
|
}
|
|
|
|
func (m *mockAgentServer) ExecuteCommand(ctx context.Context, agentID string, cmd agentexec.ExecuteCommandPayload) (*agentexec.CommandResultPayload, error) {
|
|
args := m.Called(ctx, agentID, cmd)
|
|
return args.Get(0).(*agentexec.CommandResultPayload), args.Error(1)
|
|
}
|
|
|
|
type mockMetricsHistoryProvider struct {
|
|
mock.Mock
|
|
}
|
|
|
|
func (m *mockMetricsHistoryProvider) GetResourceMetrics(resourceID string, period time.Duration) ([]MetricPoint, error) {
|
|
args := m.Called(resourceID, period)
|
|
if args.Get(0) == nil {
|
|
return nil, args.Error(1)
|
|
}
|
|
return args.Get(0).([]MetricPoint), args.Error(1)
|
|
}
|
|
|
|
func (m *mockMetricsHistoryProvider) GetAllMetricsSummary(period time.Duration) (map[string]ResourceMetricsSummary, error) {
|
|
args := m.Called(period)
|
|
return args.Get(0).(map[string]ResourceMetricsSummary), args.Error(1)
|
|
}
|
|
|
|
type mockAlertProvider struct {
|
|
mock.Mock
|
|
}
|
|
|
|
func (m *mockAlertProvider) GetActiveAlerts() []ActiveAlert {
|
|
args := m.Called()
|
|
return args.Get(0).([]ActiveAlert)
|
|
}
|
|
|
|
func (m *mockAlertProvider) GetRecentlyResolved(minutes int) []models.ResolvedAlert {
|
|
args := m.Called(minutes)
|
|
if args.Get(0) == nil {
|
|
return nil
|
|
}
|
|
return args.Get(0).([]models.ResolvedAlert)
|
|
}
|
|
|
|
type mockFindingsProvider struct {
|
|
mock.Mock
|
|
}
|
|
|
|
func (m *mockFindingsProvider) GetActiveFindings() []Finding {
|
|
args := m.Called()
|
|
return args.Get(0).([]Finding)
|
|
}
|
|
|
|
func (m *mockFindingsProvider) GetDismissedFindings() []Finding {
|
|
args := m.Called()
|
|
return args.Get(0).([]Finding)
|
|
}
|
|
|
|
type mockDiskHealthProvider struct {
|
|
mock.Mock
|
|
}
|
|
|
|
func (m *mockDiskHealthProvider) GetHosts() []*unifiedresources.HostView {
|
|
args := m.Called()
|
|
return args.Get(0).([]*unifiedresources.HostView)
|
|
}
|
|
|
|
type mockUpdatesProvider struct {
|
|
mock.Mock
|
|
}
|
|
|
|
func (m *mockUpdatesProvider) GetPendingUpdates(hostID string) []ContainerUpdateInfo {
|
|
args := m.Called(hostID)
|
|
return args.Get(0).([]ContainerUpdateInfo)
|
|
}
|
|
|
|
func (m *mockUpdatesProvider) TriggerUpdateCheck(hostID string) (DockerCommandStatus, error) {
|
|
args := m.Called(hostID)
|
|
return args.Get(0).(DockerCommandStatus), args.Error(1)
|
|
}
|
|
|
|
func (m *mockUpdatesProvider) UpdateContainer(hostID, containerID, containerName string) (DockerCommandStatus, error) {
|
|
args := m.Called(hostID, containerID, containerName)
|
|
return args.Get(0).(DockerCommandStatus), args.Error(1)
|
|
}
|
|
|
|
func (m *mockUpdatesProvider) GetCommandStatus(commandID string) (DockerCommandStatus, bool) {
|
|
args := m.Called(commandID)
|
|
return args.Get(0).(DockerCommandStatus), args.Bool(1)
|
|
}
|
|
|
|
func (m *mockUpdatesProvider) IsUpdateActionsEnabled() bool {
|
|
args := m.Called()
|
|
return args.Bool(0)
|
|
}
|
|
|
|
type mockBackupProvider struct {
|
|
mock.Mock
|
|
}
|
|
|
|
func (m *mockBackupProvider) GetBackups() models.Backups {
|
|
args := m.Called()
|
|
return args.Get(0).(models.Backups)
|
|
}
|
|
|
|
func (m *mockBackupProvider) GetPBSInstances() []models.PBSInstance {
|
|
args := m.Called()
|
|
return args.Get(0).([]models.PBSInstance)
|
|
}
|
|
|
|
// stubUnifiedResourceProvider is a simple mock for UnifiedResourceProvider.
|
|
type stubUnifiedResourceProvider struct {
|
|
resources []unifiedresources.Resource
|
|
}
|
|
|
|
func (s *stubUnifiedResourceProvider) GetByType(t unifiedresources.ResourceType) []unifiedresources.Resource {
|
|
var out []unifiedresources.Resource
|
|
for _, r := range s.resources {
|
|
if r.Type == t {
|
|
out = append(out, r)
|
|
}
|
|
}
|
|
return out
|
|
}
|