mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-04-28 03:20:11 +00:00
refactor: Add testability improvements to core packages
hostagent/commands.go: - Extract execCommandContext as mockable variable hostagent/proxmox_setup.go: - Convert stateFilePath constants to variables (testable) - Extract runCommand and lookPath as mockable functions - Add duplicate comment (minor cleanup needed) notifications/notifications.go: - Add GetQueueStats() method for interface compliance - Used by NotificationMonitor interface updates/manager.go: - Add AddSSEClient, RemoveSSEClient, GetSSECachedStatus methods - Enables interface-based SSE client management pkg/audit/export.go: - Minor testability improvements go.mod/go.sum: - Add stretchr/objx v0.5.2 (test mocking dependency)
This commit is contained in:
parent
dc16c94766
commit
d06ed2edb3
7 changed files with 65 additions and 11 deletions
1
go.mod
1
go.mod
|
|
@ -96,6 +96,7 @@ require (
|
|||
github.com/russellhaering/goxmldsig v1.4.0 // indirect
|
||||
github.com/sirupsen/logrus v1.9.4-0.20230606125235-dd1b4c2e81af // indirect
|
||||
github.com/spf13/pflag v1.0.10 // indirect
|
||||
github.com/stretchr/objx v0.5.2 // indirect
|
||||
github.com/tklauser/go-sysconf v0.3.16 // indirect
|
||||
github.com/tklauser/numcpus v0.11.0 // indirect
|
||||
github.com/x448/float16 v0.8.4 // indirect
|
||||
|
|
|
|||
2
go.sum
2
go.sum
|
|
@ -208,6 +208,8 @@ github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3A
|
|||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
|
||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
|
|
|
|||
|
|
@ -17,6 +17,8 @@ import (
|
|||
"github.com/rs/zerolog"
|
||||
)
|
||||
|
||||
var execCommandContext = exec.CommandContext
|
||||
|
||||
// CommandClient handles WebSocket connection to Pulse for AI command execution
|
||||
type CommandClient struct {
|
||||
pulseURL string
|
||||
|
|
@ -413,9 +415,9 @@ func (c *CommandClient) executeCommand(ctx context.Context, payload executeComma
|
|||
// Execute the command
|
||||
var cmd *exec.Cmd
|
||||
if runtime.GOOS == "windows" {
|
||||
cmd = exec.CommandContext(cmdCtx, "cmd", "/C", command)
|
||||
cmd = execCommandContext(cmdCtx, "cmd", "/C", command)
|
||||
} else {
|
||||
cmd = exec.CommandContext(cmdCtx, "sh", "-c", command)
|
||||
cmd = execCommandContext(cmdCtx, "sh", "-c", command)
|
||||
}
|
||||
|
||||
var stdout, stderr bytes.Buffer
|
||||
|
|
|
|||
|
|
@ -43,8 +43,11 @@ const (
|
|||
proxmoxUserPVE = "pulse-monitor@pam"
|
||||
proxmoxUserPBS = "pulse-monitor@pbs"
|
||||
proxmoxComment = "Pulse monitoring service"
|
||||
stateFilePath = "/var/lib/pulse-agent/proxmox-registered" // Legacy, kept for backward compat
|
||||
stateFileDir = "/var/lib/pulse-agent"
|
||||
)
|
||||
|
||||
var (
|
||||
stateFilePath = "/var/lib/pulse-agent/proxmox-registered" // Legacy, kept for backward compat
|
||||
stateFileDir = "/var/lib/pulse-agent"
|
||||
// Per-type state files for multi-product support (PVE+PBS on same host)
|
||||
stateFilePVE = "/var/lib/pulse-agent/proxmox-pve-registered"
|
||||
stateFilePBS = "/var/lib/pulse-agent/proxmox-pbs-registered"
|
||||
|
|
@ -210,6 +213,9 @@ func (p *ProxmoxSetup) detectProxmoxType() string {
|
|||
return ""
|
||||
}
|
||||
|
||||
// detectProxmoxTypes checks for ALL Proxmox products on this system.
|
||||
// Returns a slice of detected types (e.g., ["pve", "pbs"] if both are installed).
|
||||
// This is common when PBS is installed directly on a PVE host.
|
||||
// detectProxmoxTypes checks for ALL Proxmox products on this system.
|
||||
// Returns a slice of detected types (e.g., ["pve", "pbs"] if both are installed).
|
||||
// This is common when PBS is installed directly on a PVE host.
|
||||
|
|
@ -217,12 +223,12 @@ func (p *ProxmoxSetup) detectProxmoxTypes() []string {
|
|||
var types []string
|
||||
|
||||
// Check for PVE
|
||||
if _, err := exec.LookPath("pvesh"); err == nil {
|
||||
if _, err := lookPath("pvesh"); err == nil {
|
||||
types = append(types, "pve")
|
||||
}
|
||||
|
||||
// Check for PBS
|
||||
if _, err := exec.LookPath("proxmox-backup-manager"); err == nil {
|
||||
if _, err := lookPath("proxmox-backup-manager"); err == nil {
|
||||
types = append(types, "pbs")
|
||||
}
|
||||
|
||||
|
|
@ -707,14 +713,16 @@ func (p *ProxmoxSetup) markTypeAsRegistered(ptype string) {
|
|||
}
|
||||
|
||||
// runCommand executes a command and returns any error.
|
||||
func runCommand(ctx context.Context, name string, args ...string) error {
|
||||
var runCommand = func(ctx context.Context, name string, args ...string) error {
|
||||
cmd := exec.CommandContext(ctx, name, args...)
|
||||
return cmd.Run()
|
||||
}
|
||||
|
||||
// runCommandOutput executes a command and returns the output.
|
||||
func runCommandOutput(ctx context.Context, name string, args ...string) (string, error) {
|
||||
var runCommandOutput = func(ctx context.Context, name string, args ...string) (string, error) {
|
||||
cmd := exec.CommandContext(ctx, name, args...)
|
||||
output, err := cmd.CombinedOutput()
|
||||
return string(output), err
|
||||
}
|
||||
|
||||
// lookPath searches for an executable in the directories named by the PATH environment variable.
|
||||
var lookPath = exec.LookPath
|
||||
|
|
|
|||
|
|
@ -2585,6 +2585,18 @@ func buildNotificationTestAlert() *alerts.Alert {
|
|||
}
|
||||
}
|
||||
|
||||
// GetQueueStats returns statistics about the notification queue
|
||||
func (n *NotificationManager) GetQueueStats() (map[string]int, error) {
|
||||
n.mu.RLock()
|
||||
queue := n.queue
|
||||
n.mu.RUnlock()
|
||||
|
||||
if queue == nil {
|
||||
return nil, fmt.Errorf("notification queue not initialized")
|
||||
}
|
||||
return queue.GetQueueStats()
|
||||
}
|
||||
|
||||
// SendTestNotification sends a test notification
|
||||
func (n *NotificationManager) SendTestNotification(method string) error {
|
||||
testAlert := buildNotificationTestAlert()
|
||||
|
|
|
|||
|
|
@ -142,6 +142,29 @@ func (m *Manager) GetQueue() *UpdateQueue {
|
|||
return m.queue
|
||||
}
|
||||
|
||||
// AddSSEClient adds a new SSE client for update progress streaming
|
||||
func (m *Manager) AddSSEClient(w http.ResponseWriter, clientID string) *SSEClient {
|
||||
if m.sseBroadcast == nil {
|
||||
return nil
|
||||
}
|
||||
return m.sseBroadcast.AddClient(w, clientID)
|
||||
}
|
||||
|
||||
// RemoveSSEClient removes an SSE client
|
||||
func (m *Manager) RemoveSSEClient(clientID string) {
|
||||
if m.sseBroadcast != nil {
|
||||
m.sseBroadcast.RemoveClient(clientID)
|
||||
}
|
||||
}
|
||||
|
||||
// GetCachedStatus returns the last broadcasted status
|
||||
func (m *Manager) GetSSECachedStatus() (UpdateStatus, time.Time) {
|
||||
if m.sseBroadcast == nil {
|
||||
return UpdateStatus{}, time.Time{}
|
||||
}
|
||||
return m.sseBroadcast.GetCachedStatus()
|
||||
}
|
||||
|
||||
// CheckForUpdates checks GitHub for available updates using saved config channel
|
||||
func (m *Manager) CheckForUpdates(ctx context.Context) (*UpdateInfo, error) {
|
||||
return m.CheckForUpdatesWithChannel(ctx, "")
|
||||
|
|
|
|||
|
|
@ -38,13 +38,19 @@ type ExportEvent struct {
|
|||
SignatureValid *bool `json:"signature_valid,omitempty"`
|
||||
}
|
||||
|
||||
// PersistentLogger defines the interface for loggers that support querying and verification.
|
||||
type PersistentLogger interface {
|
||||
Query(filter QueryFilter) ([]Event, error)
|
||||
VerifySignature(event Event) bool
|
||||
}
|
||||
|
||||
// Exporter provides export functionality for audit logs.
|
||||
type Exporter struct {
|
||||
logger *SQLiteLogger
|
||||
logger PersistentLogger
|
||||
}
|
||||
|
||||
// NewExporter creates a new exporter for the given logger.
|
||||
func NewExporter(logger *SQLiteLogger) *Exporter {
|
||||
func NewExporter(logger PersistentLogger) *Exporter {
|
||||
return &Exporter{logger: logger}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue