fix(node): support timestamped remote log queries
Expose a Since-aware remote log helper on the controller and plumb the filter through the worker's miner log lookup so the payload field is honoured end-to-end. Co-Authored-By: Virgil <virgil@lethean.io>
This commit is contained in:
parent
727b5fdb8d
commit
8d1caa3a59
4 changed files with 73 additions and 8 deletions
|
|
@ -210,6 +210,11 @@ func (c *Controller) StopRemoteMiner(peerID, minerName string) error {
|
|||
|
||||
// GetRemoteLogs requests console logs from a remote miner.
|
||||
func (c *Controller) GetRemoteLogs(peerID, minerName string, lines int) ([]string, error) {
|
||||
return c.GetRemoteLogsSince(peerID, minerName, lines, time.Time{})
|
||||
}
|
||||
|
||||
// GetRemoteLogsSince requests console logs from a remote miner after a point in time.
|
||||
func (c *Controller) GetRemoteLogsSince(peerID, minerName string, lines int, since time.Time) ([]string, error) {
|
||||
identity := c.node.GetIdentity()
|
||||
if identity == nil {
|
||||
return nil, ErrIdentityNotInitialized
|
||||
|
|
@ -219,10 +224,13 @@ func (c *Controller) GetRemoteLogs(peerID, minerName string, lines int) ([]strin
|
|||
MinerName: minerName,
|
||||
Lines: lines,
|
||||
}
|
||||
if !since.IsZero() {
|
||||
payload.Since = since.UnixMilli()
|
||||
}
|
||||
|
||||
msg, err := NewMessage(MsgGetLogs, identity.ID, peerID, payload)
|
||||
if err != nil {
|
||||
return nil, coreerr.E("Controller.GetRemoteLogs", "failed to create message", err)
|
||||
return nil, coreerr.E("Controller.GetRemoteLogsSince", "failed to create message", err)
|
||||
}
|
||||
|
||||
resp, err := c.sendRequest(peerID, msg, 10*time.Second)
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import (
|
|||
"net/http/httptest"
|
||||
"net/url"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
|
|
@ -514,6 +515,40 @@ type mockMinerFull struct {
|
|||
func (m *mockMinerFull) GetName() string { return m.name }
|
||||
func (m *mockMinerFull) GetType() string { return m.minerType }
|
||||
func (m *mockMinerFull) GetStats() (any, error) { return m.stats, nil }
|
||||
func (m *mockMinerFull) GetConsoleHistorySince(lines int, since time.Time) []string {
|
||||
if since.IsZero() {
|
||||
if lines >= len(m.consoleHistory) {
|
||||
return m.consoleHistory
|
||||
}
|
||||
return m.consoleHistory[:lines]
|
||||
}
|
||||
|
||||
filtered := make([]string, 0, len(m.consoleHistory))
|
||||
for _, line := range m.consoleHistory {
|
||||
if lineAfter(line, since) {
|
||||
filtered = append(filtered, line)
|
||||
}
|
||||
}
|
||||
if lines >= len(filtered) {
|
||||
return filtered
|
||||
}
|
||||
return filtered[:lines]
|
||||
}
|
||||
|
||||
func lineAfter(line string, since time.Time) bool {
|
||||
start := strings.IndexByte(line, '[')
|
||||
end := strings.IndexByte(line, ']')
|
||||
if start != 0 || end <= start+1 {
|
||||
return true
|
||||
}
|
||||
|
||||
ts, err := time.Parse("2006-01-02 15:04:05", line[start+1:end])
|
||||
if err != nil {
|
||||
return true
|
||||
}
|
||||
return ts.After(since) || ts.Equal(since)
|
||||
}
|
||||
|
||||
func (m *mockMinerFull) GetConsoleHistory(lines int) []string {
|
||||
if lines >= len(m.consoleHistory) {
|
||||
return m.consoleHistory
|
||||
|
|
@ -616,6 +651,20 @@ func TestController_GetRemoteLogs_LimitedLines(t *testing.T) {
|
|||
assert.Len(t, lines, 1, "should return only 1 line")
|
||||
}
|
||||
|
||||
func TestController_GetRemoteLogsSince(t *testing.T) {
|
||||
controller, _, tp := setupControllerPairWithMiner(t)
|
||||
serverID := tp.ServerNode.GetIdentity().ID
|
||||
|
||||
since, err := time.Parse("2006-01-02 15:04:05", "2026-02-20 10:00:01")
|
||||
require.NoError(t, err)
|
||||
|
||||
lines, err := controller.GetRemoteLogsSince(serverID, "running-miner", 10, since)
|
||||
require.NoError(t, err, "GetRemoteLogsSince should succeed")
|
||||
require.Len(t, lines, 2, "should return only log lines on or after the requested timestamp")
|
||||
assert.Contains(t, lines[0], "connected to pool")
|
||||
assert.Contains(t, lines[1], "new job received")
|
||||
}
|
||||
|
||||
func TestController_GetRemoteLogs_NoIdentity(t *testing.T) {
|
||||
tp := setupTestTransportPair(t)
|
||||
nmNoID, err := NewNodeManagerWithPaths(
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ type MinerInstance interface {
|
|||
GetName() string
|
||||
GetType() string
|
||||
GetStats() (any, error)
|
||||
GetConsoleHistory(lines int) []string
|
||||
GetConsoleHistorySince(lines int, since time.Time) []string
|
||||
}
|
||||
|
||||
// ProfileManager interface for profile operations.
|
||||
|
|
@ -55,7 +55,6 @@ func NewWorker(node *NodeManager, transport *Transport) *Worker {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
// SetMinerManager sets the miner manager for handling miner operations.
|
||||
func (w *Worker) SetMinerManager(manager MinerManager) {
|
||||
w.minerManager = manager
|
||||
|
|
@ -286,7 +285,12 @@ func (w *Worker) handleGetLogs(msg *Message) (*Message, error) {
|
|||
return nil, coreerr.E("Worker.handleGetLogs", "miner not found: "+payload.MinerName, nil)
|
||||
}
|
||||
|
||||
lines := miner.GetConsoleHistory(payload.Lines)
|
||||
var since time.Time
|
||||
if payload.Since > 0 {
|
||||
since = time.UnixMilli(payload.Since)
|
||||
}
|
||||
|
||||
lines := miner.GetConsoleHistorySince(payload.Lines, since)
|
||||
|
||||
logs := LogsPayload{
|
||||
MinerName: payload.MinerName,
|
||||
|
|
|
|||
|
|
@ -550,10 +550,14 @@ type mockMinerInstance struct {
|
|||
stats any
|
||||
}
|
||||
|
||||
func (m *mockMinerInstance) GetName() string { return m.name }
|
||||
func (m *mockMinerInstance) GetType() string { return m.minerType }
|
||||
func (m *mockMinerInstance) GetStats() (any, error) { return m.stats, nil }
|
||||
func (m *mockMinerInstance) GetConsoleHistory(lines int) []string { return []string{} }
|
||||
func (m *mockMinerInstance) GetName() string { return m.name }
|
||||
func (m *mockMinerInstance) GetType() string { return m.minerType }
|
||||
func (m *mockMinerInstance) GetStats() (any, error) {
|
||||
return m.stats, nil
|
||||
}
|
||||
func (m *mockMinerInstance) GetConsoleHistorySince(lines int, since time.Time) []string {
|
||||
return []string{}
|
||||
}
|
||||
|
||||
type mockProfileManager struct{}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue