diff --git a/pkg/manager/manager.go b/pkg/manager/manager.go index 56df75a..b944ef3 100644 --- a/pkg/manager/manager.go +++ b/pkg/manager/manager.go @@ -26,7 +26,7 @@ type InstanceManager interface { StopInstance(name string) (*instance.Process, error) EvictLRUInstance() error RestartInstance(name string) (*instance.Process, error) - GetInstanceLogs(name string) (string, error) + GetInstanceLogs(name string, numLines int) (string, error) Shutdown() } @@ -39,7 +39,7 @@ type RemoteManager interface { StartRemoteInstance(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) - GetRemoteInstanceLogs(node *config.NodeConfig, name string) (string, error) + GetRemoteInstanceLogs(node *config.NodeConfig, name string, numLines int) (string, error) } type instanceManager struct { diff --git a/pkg/manager/operations.go b/pkg/manager/operations.go index 3bce1e6..2d95ef1 100644 --- a/pkg/manager/operations.go +++ b/pkg/manager/operations.go @@ -13,15 +13,33 @@ import ( type MaxRunningInstancesError error // 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) { im.mu.RLock() - defer im.mu.RUnlock() - - instances := make([]*instance.Process, 0, len(im.instances)) + localInstances := make([]*instance.Process, 0, len(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. @@ -115,6 +133,7 @@ func (im *instanceManager) CreateInstance(name string, options *instance.CreateI } // 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) { im.mu.RLock() 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) } - // 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 { - 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 @@ -401,7 +431,7 @@ func (im *instanceManager) RestartInstance(name string) (*instance.Process, erro } // 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() inst, exists := im.instances[name] 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 if node := im.getNodeForInstance(inst); node != nil { - return im.GetRemoteInstanceLogs(node, name) + return im.GetRemoteInstanceLogs(node, name, numLines) } - // TODO: Implement actual log retrieval logic - return fmt.Sprintf("Logs for instance %s", name), nil + // Get logs from the local instance + return inst.GetLogs(numLines) } // getPortFromOptions extracts the port from backend-specific options diff --git a/pkg/manager/remote_ops.go b/pkg/manager/remote_ops.go index 49b24f1..40b2384 100644 --- a/pkg/manager/remote_ops.go +++ b/pkg/manager/remote_ops.go @@ -211,8 +211,8 @@ func (im *instanceManager) RestartRemoteInstance(nodeConfig *config.NodeConfig, } // GetRemoteInstanceLogs retrieves logs for an instance from the remote node -func (im *instanceManager) GetRemoteInstanceLogs(nodeConfig *config.NodeConfig, name string) (string, error) { - path := fmt.Sprintf("/api/v1/instances/%s/logs", name) +func (im *instanceManager) GetRemoteInstanceLogs(nodeConfig *config.NodeConfig, name string, numLines int) (string, error) { + path := fmt.Sprintf("/api/v1/instances/%s/logs?lines=%d", name, numLines) resp, err := im.makeRemoteRequest(nodeConfig, "GET", path, nil) if err != nil { return "", err diff --git a/pkg/server/handlers_instances.go b/pkg/server/handlers_instances.go index 88e974f..be3cf4a 100644 --- a/pkg/server/handlers_instances.go +++ b/pkg/server/handlers_instances.go @@ -308,23 +308,18 @@ func (h *Handler) GetInstanceLogs() http.HandlerFunc { } lines := r.URL.Query().Get("lines") - if lines == "" { - lines = "-1" + numLines := -1 // Default to all lines + 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) - if err != nil { - 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) + // Use the instance manager which handles both local and remote instances + logs, err := h.InstanceManager.GetInstanceLogs(name, numLines) if err != nil { http.Error(w, "Failed to get logs: "+err.Error(), http.StatusInternalServerError) return