mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-10-23 20:25:56 +00:00
Merge pull request #2177 from jamincollins/fix/disable-proxmox-peer-sensors
feat(agent): allow disabling Proxmox peer sensor collection
This commit is contained in:
+40
-35
@@ -590,38 +590,39 @@ func run(ctx context.Context, args []string, getenv func(string) string) error {
|
||||
}
|
||||
}
|
||||
hostCfg := hostagent.Config{
|
||||
PulseURL: cfg.PulseURL,
|
||||
APIToken: cfg.APIToken,
|
||||
Interval: cfg.Interval,
|
||||
HostnameOverride: cfg.HostnameOverride,
|
||||
AgentID: cfg.AgentID,
|
||||
AgentType: "unified",
|
||||
AgentVersion: Version,
|
||||
Tags: cfg.Tags,
|
||||
InsecureSkipVerify: cfg.InsecureSkipVerify,
|
||||
CACertPath: cfg.CACertPath,
|
||||
ServerFingerprint: cfg.ServerFingerprint,
|
||||
CustomSensorsFile: cfg.CustomSensorsFile,
|
||||
DeploySSHUser: cfg.DeploySSHUser,
|
||||
LogLevel: cfg.LogLevel,
|
||||
Logger: &logger,
|
||||
EnableProxmox: cfg.EnableProxmox,
|
||||
ProxmoxType: cfg.ProxmoxType,
|
||||
EnableCommands: cfg.EnableCommands,
|
||||
CommandAuthorityProfile: cfg.CommandAuthorityProfile,
|
||||
Enroll: cfg.Enroll,
|
||||
DiskExclude: cfg.DiskExclude,
|
||||
DiskInclude: cfg.DiskInclude,
|
||||
StateDir: cfg.StateDir,
|
||||
ReportIP: cfg.ReportIP,
|
||||
DisableCeph: cfg.DisableCeph,
|
||||
AvailabilityTargets: cfg.AvailabilityTargets,
|
||||
AppliedConfig: cfg.AppliedConfig,
|
||||
ModuleStatus: runtimeStatus.moduleStatuses,
|
||||
PrivilegeHelperStatus: privilegeHelperStatus,
|
||||
Observers: hostObserverTargets(cfg.Observers),
|
||||
PrivilegedTelemetry: privilegedTelemetry,
|
||||
UpdatedFromVersion: pendingUpdatePreviousVersion(pendingUpdate),
|
||||
PulseURL: cfg.PulseURL,
|
||||
APIToken: cfg.APIToken,
|
||||
Interval: cfg.Interval,
|
||||
HostnameOverride: cfg.HostnameOverride,
|
||||
AgentID: cfg.AgentID,
|
||||
AgentType: "unified",
|
||||
AgentVersion: Version,
|
||||
Tags: cfg.Tags,
|
||||
InsecureSkipVerify: cfg.InsecureSkipVerify,
|
||||
CACertPath: cfg.CACertPath,
|
||||
ServerFingerprint: cfg.ServerFingerprint,
|
||||
CustomSensorsFile: cfg.CustomSensorsFile,
|
||||
DeploySSHUser: cfg.DeploySSHUser,
|
||||
LogLevel: cfg.LogLevel,
|
||||
Logger: &logger,
|
||||
EnableProxmox: cfg.EnableProxmox,
|
||||
ProxmoxType: cfg.ProxmoxType,
|
||||
EnableCommands: cfg.EnableCommands,
|
||||
CommandAuthorityProfile: cfg.CommandAuthorityProfile,
|
||||
Enroll: cfg.Enroll,
|
||||
DiskExclude: cfg.DiskExclude,
|
||||
DiskInclude: cfg.DiskInclude,
|
||||
StateDir: cfg.StateDir,
|
||||
ReportIP: cfg.ReportIP,
|
||||
DisableCeph: cfg.DisableCeph,
|
||||
DisableClusterPeerSensors: cfg.DisableClusterPeerSensors,
|
||||
AvailabilityTargets: cfg.AvailabilityTargets,
|
||||
AppliedConfig: cfg.AppliedConfig,
|
||||
ModuleStatus: runtimeStatus.moduleStatuses,
|
||||
PrivilegeHelperStatus: privilegeHelperStatus,
|
||||
Observers: hostObserverTargets(cfg.Observers),
|
||||
PrivilegedTelemetry: privilegedTelemetry,
|
||||
UpdatedFromVersion: pendingUpdatePreviousVersion(pendingUpdate),
|
||||
|
||||
DockerContainerUpdater: dockerUpdaterBridge,
|
||||
DockerContainerLifecycleOperator: dockerUpdaterBridge,
|
||||
@@ -1106,9 +1107,10 @@ type Config struct {
|
||||
DiskInclude []string // Devices or mount points to opt into monitoring despite automatic filtering
|
||||
|
||||
// Network configuration
|
||||
ReportIP string // IP address to report (for multi-NIC systems)
|
||||
DisableCeph bool // Disable local Ceph status polling
|
||||
SelfTest bool // Perform self-test and exit
|
||||
ReportIP string // IP address to report (for multi-NIC systems)
|
||||
DisableCeph bool // Disable local Ceph status polling
|
||||
DisableClusterPeerSensors bool // Keep Proxmox monitoring local and skip SSH sensor collection from cluster peers
|
||||
SelfTest bool // Perform self-test and exit
|
||||
|
||||
// AvailabilityTargets are externally probed availability checks assigned to
|
||||
// this agent by the server. Remote config is the only source.
|
||||
@@ -1242,6 +1244,7 @@ func loadConfig(args []string, getenv func(string) string) (Config, error) {
|
||||
envDiskInclude := strings.TrimSpace(getenv("PULSE_DISK_INCLUDE"))
|
||||
envReportIP := strings.TrimSpace(getenv("PULSE_REPORT_IP"))
|
||||
envDisableCeph := strings.TrimSpace(getenv("PULSE_DISABLE_CEPH"))
|
||||
envDisableClusterPeerSensors := strings.TrimSpace(getenv("PULSE_DISABLE_CLUSTER_PEER_SENSORS"))
|
||||
envObserversFile := strings.TrimSpace(getenv("PULSE_OBSERVERS_FILE"))
|
||||
envCustomSensorsFile := strings.TrimSpace(getenv("PULSE_CUSTOM_SENSORS_FILE"))
|
||||
|
||||
@@ -1325,6 +1328,7 @@ func loadConfig(args []string, getenv func(string) string) (Config, error) {
|
||||
stateDirFlag := fs.String("state-dir", envStateDir, "Persistent state directory (default: platform service state directory)")
|
||||
reportIPFlag := fs.String("report-ip", envReportIP, "IP address to report (for multi-NIC systems)")
|
||||
disableCephFlag := fs.Bool("disable-ceph", utils.ParseBool(envDisableCeph), "Disable local Ceph status polling")
|
||||
disableClusterPeerSensorsFlag := fs.Bool("disable-cluster-peer-sensors", utils.ParseBool(envDisableClusterPeerSensors), "Skip SSH sensor collection from Proxmox cluster peers")
|
||||
showVersion := fs.Bool("version", false, "Print the agent version and exit")
|
||||
selfTest := fs.Bool("self-test", false, "Perform self-test and exit (used during auto-update)")
|
||||
|
||||
@@ -1496,6 +1500,7 @@ func loadConfig(args []string, getenv func(string) string) (Config, error) {
|
||||
DiskInclude: diskInclude,
|
||||
ReportIP: strings.TrimSpace(*reportIPFlag),
|
||||
DisableCeph: *disableCephFlag,
|
||||
DisableClusterPeerSensors: *disableClusterPeerSensorsFlag,
|
||||
SelfTest: *selfTest,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -348,6 +348,16 @@ func TestApplyRemoteSettings(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadConfigDisableClusterPeerSensorsFlag(t *testing.T) {
|
||||
cfg, err := loadConfig([]string{"--disable-cluster-peer-sensors"}, func(string) string { return "" })
|
||||
if err != nil {
|
||||
t.Fatalf("loadConfig returned unexpected error: %v", err)
|
||||
}
|
||||
if !cfg.DisableClusterPeerSensors {
|
||||
t.Fatal("expected --disable-cluster-peer-sensors to disable peer sensor collection")
|
||||
}
|
||||
}
|
||||
|
||||
func TestApplyRemoteSettingsHonorsLocalDockerOptOut(t *testing.T) {
|
||||
logger := zerolog.New(io.Discard)
|
||||
cfg := &Config{
|
||||
|
||||
@@ -19,6 +19,12 @@ verification guidance.
|
||||
|
||||
> Note: For agent-based temperature monitoring, use `pulse-agent --enable-proxmox` or SSH-based collection. The legacy sensor proxy has been removed. See `docs/TEMPERATURE_MONITORING.md`.
|
||||
|
||||
When each Proxmox cluster node runs an agent, add
|
||||
`--disable-cluster-peer-sensors` (or set
|
||||
`PULSE_DISABLE_CLUSTER_PEER_SENSORS=true`) to keep temperature collection local
|
||||
to each node. This preserves local host telemetry and Proxmox integration while
|
||||
preventing duplicate root-SSH sensor polling between cluster peers.
|
||||
|
||||
## Quick Start
|
||||
|
||||
Generate an installation command in the UI:
|
||||
|
||||
@@ -164,6 +164,15 @@ management, and fleet control surfaces. Pulse v6 has one host-installed
|
||||
infrastructure agent binary, `pulse-agent`; host, Docker / Podman,
|
||||
Kubernetes, Proxmox-local, and other node-local telemetry are modules inside
|
||||
that binary, not separate customer-facing agent products.
|
||||
|
||||
In a Proxmox VE cluster, an enabled agent may collect peer temperature data
|
||||
through its existing SSH sensor path. `--disable-cluster-peer-sensors` and
|
||||
`PULSE_DISABLE_CLUSTER_PEER_SENSORS=true` are local, default-off operator
|
||||
controls that stop only that peer discovery and SSH collection path. They do
|
||||
not disable the local host, SMART, Ceph, or Proxmox reporting modules, alter
|
||||
agent enrollment or identity, or add remote command authority. The disabled
|
||||
path is pinned by `TestCollectClusterSensors_Disabled`; configuration parsing
|
||||
is pinned by `TestLoadConfigDisableClusterPeerSensorsFlag`.
|
||||
On supported Linux systemd hosts, the opt-in safe runtime is a root-owned,
|
||||
unprivileged monitoring collector plus the no-network typed helper, with
|
||||
remediation installed only as the separate root-owned `pulse-agent-runner`.
|
||||
|
||||
@@ -202,6 +202,11 @@ configuration. It may override Pulse's automatic filesystem suppression only;
|
||||
it must not override a matching operator exclusion or introduce remote
|
||||
authority to expand the reported filesystem set silently.
|
||||
|
||||
The local `--disable-cluster-peer-sensors` control may suppress only the
|
||||
agent's outbound Proxmox peer sensor SSH probes. It neither accepts a remote
|
||||
command nor changes SSH credentials, target authority, agent scopes, token
|
||||
handling, enrollment, or the local host/SMART/Ceph/Proxmox reporting boundary.
|
||||
|
||||
Retained Patrol objective briefs and optional context are operator-authored AI
|
||||
content. They are encrypted at rest in the organization-scoped Pulse data
|
||||
directory and loading fails closed if decryption fails. Audit and telemetry
|
||||
|
||||
@@ -19,6 +19,12 @@ verification guidance.
|
||||
|
||||
> Note: For agent-based temperature monitoring, use `pulse-agent --enable-proxmox` or SSH-based collection. The legacy sensor proxy has been removed. See `docs/TEMPERATURE_MONITORING.md`.
|
||||
|
||||
When each Proxmox cluster node runs an agent, add
|
||||
`--disable-cluster-peer-sensors` (or set
|
||||
`PULSE_DISABLE_CLUSTER_PEER_SENSORS=true`) to keep temperature collection local
|
||||
to each node. This preserves local host telemetry and Proxmox integration while
|
||||
preventing duplicate root-SSH sensor polling between cluster peers.
|
||||
|
||||
## Quick Start
|
||||
|
||||
Generate an installation command in the UI:
|
||||
|
||||
@@ -80,8 +80,9 @@ type Config struct {
|
||||
DeploySSHUser string // Default: root. Non-root users must support passwordless sudo for install steps.
|
||||
|
||||
// Network configuration
|
||||
ReportIP string // IP address to report instead of auto-detected (for multi-NIC systems)
|
||||
DisableCeph bool // If true, disables local Ceph status polling
|
||||
ReportIP string // IP address to report instead of auto-detected (for multi-NIC systems)
|
||||
DisableCeph bool // If true, disables local Ceph status polling
|
||||
DisableClusterPeerSensors bool // If true, keeps Proxmox monitoring local and skips SSH sensor collection from cluster peers
|
||||
|
||||
// AvailabilityTargets are the externally probed availability checks the
|
||||
// server assigned to this agent at startup. Later assignments arrive
|
||||
|
||||
@@ -60,6 +60,28 @@ func TestNew_AllowsMissingAPITokenWhenEnrollmentDisabled(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestNew_PreservesDisableClusterPeerSensors(t *testing.T) {
|
||||
mc := &mockCollector{
|
||||
hostInfoFn: func(context.Context) (*gohost.InfoStat, error) {
|
||||
return &gohost.InfoStat{Hostname: "pve-host", HostID: "hid", KernelArch: runtime.GOARCH}, nil
|
||||
},
|
||||
}
|
||||
|
||||
agent, err := New(Config{
|
||||
APIToken: "token",
|
||||
EnableProxmox: true,
|
||||
DisableClusterPeerSensors: true,
|
||||
LogLevel: zerolog.InfoLevel,
|
||||
Collector: mc,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("New: %v", err)
|
||||
}
|
||||
if !agent.cfg.DisableClusterPeerSensors {
|
||||
t.Fatal("expected peer sensor collection opt-out to be preserved")
|
||||
}
|
||||
}
|
||||
|
||||
func TestNew_AllowsLocalNetworkHTTPPulseURL(t *testing.T) {
|
||||
mc := &mockCollector{
|
||||
hostInfoFn: func(context.Context) (*gohost.InfoStat, error) {
|
||||
|
||||
@@ -36,7 +36,7 @@ const (
|
||||
// collectClusterSensors discovers Proxmox cluster siblings and collects temperature
|
||||
// data from each via SSH. Returns nil if not in a cluster or no peers are reachable.
|
||||
func (a *Agent) collectClusterSensors(ctx context.Context) []agentshost.ClusterNodeSensors {
|
||||
if !a.cfg.EnableProxmox {
|
||||
if !a.cfg.EnableProxmox || a.cfg.DisableClusterPeerSensors {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -182,6 +182,36 @@ func TestCollectClusterSensors_NotProxmox(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestCollectClusterSensors_Disabled(t *testing.T) {
|
||||
logger := zerolog.New(zerolog.NewConsoleWriter(func(w *zerolog.ConsoleWriter) {
|
||||
w.Out = os.Stderr
|
||||
})).Level(zerolog.DebugLevel)
|
||||
|
||||
commandCalled := false
|
||||
a := &Agent{
|
||||
logger: logger,
|
||||
cfg: Config{
|
||||
EnableProxmox: true,
|
||||
DisableClusterPeerSensors: true,
|
||||
},
|
||||
collector: &mockCollector{
|
||||
goos: "linux",
|
||||
commandCombinedOutputFn: func(ctx context.Context, name string, arg ...string) (string, error) {
|
||||
commandCalled = true
|
||||
return "", fmt.Errorf("cluster peer collection must be disabled")
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
result := a.collectClusterSensors(context.Background())
|
||||
if result != nil {
|
||||
t.Errorf("expected nil when cluster peer sensors are disabled, got %v", result)
|
||||
}
|
||||
if commandCalled {
|
||||
t.Error("cluster peer collection ran despite being disabled")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCollectClusterSensors_NotLinux(t *testing.T) {
|
||||
logger := zerolog.New(zerolog.NewConsoleWriter(func(w *zerolog.ConsoleWriter) {
|
||||
w.Out = os.Stderr
|
||||
|
||||
Reference in New Issue
Block a user