Fix getting remote instance logs

This commit is contained in:
2025-10-09 20:22:32 +02:00
parent 9684a8a09b
commit 8a16a195de
4 changed files with 55 additions and 30 deletions

View File

@@ -26,7 +26,7 @@ type InstanceManager interface {
StopInstance(name string) (*instance.Process, error) StopInstance(name string) (*instance.Process, error)
EvictLRUInstance() error EvictLRUInstance() error
RestartInstance(name string) (*instance.Process, error) RestartInstance(name string) (*instance.Process, error)
GetInstanceLogs(name string) (string, error) GetInstanceLogs(name string, numLines int) (string, error)
Shutdown() Shutdown()
} }
@@ -39,7 +39,7 @@ type RemoteManager interface {
StartRemoteInstance(node *config.NodeConfig, name string) (*instance.Process, error) StartRemoteInstance(node *config.NodeConfig, name string) (*instance.Process, error)
StopRemoteInstance(node *config.NodeConfig, name string) (*instance.Process, error) StopRemoteInstance(node *config.NodeConfig, name string) (*instance.Process, error)
RestartRemoteInstance(node *config.NodeConfig, name string) (*instance.Process, error) RestartRemoteInstance(node *config.NodeConfig, name string) (*instance.Process, error)
GetRemoteInstanceLogs(node *config.NodeConfig, name string) (string, error) GetRemoteInstanceLogs(node *config.NodeConfig, name string, numLines int) (string, error)
} }
type instanceManager struct { type instanceManager struct {

View File

@@ -13,15 +13,33 @@ import (
type MaxRunningInstancesError error type MaxRunningInstancesError error
// ListInstances returns a list of all instances managed by the instance manager. // ListInstances returns a list of all instances managed by the instance manager.
// For remote instances, this fetches the live state from remote nodes and updates local stubs.
func (im *instanceManager) ListInstances() ([]*instance.Process, error) { func (im *instanceManager) ListInstances() ([]*instance.Process, error) {
im.mu.RLock() im.mu.RLock()
defer im.mu.RUnlock() localInstances := make([]*instance.Process, 0, len(im.instances))
instances := make([]*instance.Process, 0, len(im.instances))
for _, inst := range im.instances { for _, inst := range im.instances {
instances = append(instances, inst) localInstances = append(localInstances, inst)
} }
return instances, nil im.mu.RUnlock()
// Update remote instances with live state
for _, inst := range localInstances {
if node := im.getNodeForInstance(inst); node != nil {
remoteInst, err := im.GetRemoteInstance(node, inst.Name)
if err != nil {
// Log error but continue with stale data
// Don't fail the entire list operation due to one remote failure
continue
}
// Update the local stub's status to reflect remote state
im.mu.Lock()
inst.Status = remoteInst.Status
im.mu.Unlock()
}
}
return localInstances, nil
} }
// CreateInstance creates a new instance with the given options and returns it. // CreateInstance creates a new instance with the given options and returns it.
@@ -115,6 +133,7 @@ func (im *instanceManager) CreateInstance(name string, options *instance.CreateI
} }
// GetInstance retrieves an instance by its name. // GetInstance retrieves an instance by its name.
// For remote instances, this fetches the live state from the remote node and updates the local stub.
func (im *instanceManager) GetInstance(name string) (*instance.Process, error) { func (im *instanceManager) GetInstance(name string) (*instance.Process, error) {
im.mu.RLock() im.mu.RLock()
inst, exists := im.instances[name] inst, exists := im.instances[name]
@@ -124,9 +143,20 @@ func (im *instanceManager) GetInstance(name string) (*instance.Process, error) {
return nil, fmt.Errorf("instance with name %s not found", name) return nil, fmt.Errorf("instance with name %s not found", name)
} }
// Check if instance is remote and delegate to remote operation // Check if instance is remote and fetch live state
if node := im.getNodeForInstance(inst); node != nil { if node := im.getNodeForInstance(inst); node != nil {
return im.GetRemoteInstance(node, name) remoteInst, err := im.GetRemoteInstance(node, name)
if err != nil {
return nil, err
}
// Update the local stub's status to reflect remote state
im.mu.Lock()
inst.Status = remoteInst.Status
im.mu.Unlock()
// Return the local stub (preserving Nodes field)
return inst, nil
} }
return inst, nil return inst, nil
@@ -401,7 +431,7 @@ func (im *instanceManager) RestartInstance(name string) (*instance.Process, erro
} }
// GetInstanceLogs retrieves the logs for a specific instance by its name. // GetInstanceLogs retrieves the logs for a specific instance by its name.
func (im *instanceManager) GetInstanceLogs(name string) (string, error) { func (im *instanceManager) GetInstanceLogs(name string, numLines int) (string, error) {
im.mu.RLock() im.mu.RLock()
inst, exists := im.instances[name] inst, exists := im.instances[name]
im.mu.RUnlock() im.mu.RUnlock()
@@ -412,11 +442,11 @@ func (im *instanceManager) GetInstanceLogs(name string) (string, error) {
// Check if instance is remote and delegate to remote operation // Check if instance is remote and delegate to remote operation
if node := im.getNodeForInstance(inst); node != nil { if node := im.getNodeForInstance(inst); node != nil {
return im.GetRemoteInstanceLogs(node, name) return im.GetRemoteInstanceLogs(node, name, numLines)
} }
// TODO: Implement actual log retrieval logic // Get logs from the local instance
return fmt.Sprintf("Logs for instance %s", name), nil return inst.GetLogs(numLines)
} }
// getPortFromOptions extracts the port from backend-specific options // getPortFromOptions extracts the port from backend-specific options

View File

@@ -211,8 +211,8 @@ func (im *instanceManager) RestartRemoteInstance(nodeConfig *config.NodeConfig,
} }
// GetRemoteInstanceLogs retrieves logs for an instance from the remote node // GetRemoteInstanceLogs retrieves logs for an instance from the remote node
func (im *instanceManager) GetRemoteInstanceLogs(nodeConfig *config.NodeConfig, name string) (string, error) { func (im *instanceManager) GetRemoteInstanceLogs(nodeConfig *config.NodeConfig, name string, numLines int) (string, error) {
path := fmt.Sprintf("/api/v1/instances/%s/logs", name) path := fmt.Sprintf("/api/v1/instances/%s/logs?lines=%d", name, numLines)
resp, err := im.makeRemoteRequest(nodeConfig, "GET", path, nil) resp, err := im.makeRemoteRequest(nodeConfig, "GET", path, nil)
if err != nil { if err != nil {
return "", err return "", err

View File

@@ -308,23 +308,18 @@ func (h *Handler) GetInstanceLogs() http.HandlerFunc {
} }
lines := r.URL.Query().Get("lines") lines := r.URL.Query().Get("lines")
if lines == "" { numLines := -1 // Default to all lines
lines = "-1" if lines != "" {
parsedLines, err := strconv.Atoi(lines)
if err != nil {
http.Error(w, "Invalid lines parameter: "+err.Error(), http.StatusBadRequest)
return
}
numLines = parsedLines
} }
num_lines, err := strconv.Atoi(lines) // Use the instance manager which handles both local and remote instances
if err != nil { logs, err := h.InstanceManager.GetInstanceLogs(name, numLines)
http.Error(w, "Invalid lines parameter: "+err.Error(), http.StatusBadRequest)
return
}
inst, err := h.InstanceManager.GetInstance(name)
if err != nil {
http.Error(w, "Failed to get instance: "+err.Error(), http.StatusInternalServerError)
return
}
logs, err := inst.GetLogs(num_lines)
if err != nil { if err != nil {
http.Error(w, "Failed to get logs: "+err.Error(), http.StatusInternalServerError) http.Error(w, "Failed to get logs: "+err.Error(), http.StatusInternalServerError)
return return