Refactor instance status management: replace Running boolean with InstanceStatus enum and update related methods

This commit is contained in:
2025-08-27 19:44:38 +02:00
parent 615c2ac54e
commit 1443746add
10 changed files with 111 additions and 45 deletions

View File

@@ -82,7 +82,7 @@ type Process struct {
globalSettings *config.InstancesConfig
// Status
Running bool `json:"running"`
Status InstanceStatus `json:"status"`
// Creation time
Created int64 `json:"created,omitempty"` // Unix timestamp when the instance was created
@@ -287,12 +287,12 @@ func (i *Process) MarshalJSON() ([]byte, error) {
temp := struct {
Name string `json:"name"`
Options *CreateInstanceOptions `json:"options,omitempty"`
Running bool `json:"running"`
Status InstanceStatus `json:"status"`
Created int64 `json:"created,omitempty"`
}{
Name: i.Name,
Options: i.options,
Running: i.Running,
Status: i.Status,
Created: i.Created,
}
@@ -305,7 +305,7 @@ func (i *Process) UnmarshalJSON(data []byte) error {
temp := struct {
Name string `json:"name"`
Options *CreateInstanceOptions `json:"options,omitempty"`
Running bool `json:"running"`
Status InstanceStatus `json:"status"`
Created int64 `json:"created,omitempty"`
}{}
@@ -315,7 +315,7 @@ func (i *Process) UnmarshalJSON(data []byte) error {
// Set the fields
i.Name = temp.Name
i.Running = temp.Running
i.Status = temp.Status
i.Created = temp.Created
// Handle options with validation but no defaults

View File

@@ -29,7 +29,7 @@ func TestNewInstance(t *testing.T) {
if instance.Name != "test-instance" {
t.Errorf("Expected name 'test-instance', got %q", instance.Name)
}
if instance.Running {
if instance.IsRunning() {
t.Error("New instance should not be running")
}
@@ -188,7 +188,7 @@ func TestMarshalJSON(t *testing.T) {
}
// Check that JSON contains expected fields
var result map[string]interface{}
var result map[string]any
err = json.Unmarshal(data, &result)
if err != nil {
t.Fatalf("JSON unmarshal failed: %v", err)
@@ -197,8 +197,8 @@ func TestMarshalJSON(t *testing.T) {
if result["name"] != "test-instance" {
t.Errorf("Expected name 'test-instance', got %v", result["name"])
}
if result["running"] != false {
t.Errorf("Expected running false, got %v", result["running"])
if result["status"] != "stopped" {
t.Errorf("Expected status 'stopped', got %v", result["status"])
}
// Check that options are included
@@ -218,7 +218,7 @@ func TestMarshalJSON(t *testing.T) {
func TestUnmarshalJSON(t *testing.T) {
jsonData := `{
"name": "test-instance",
"running": true,
"status": "running",
"options": {
"model": "/path/to/model.gguf",
"port": 8080,
@@ -236,8 +236,8 @@ func TestUnmarshalJSON(t *testing.T) {
if inst.Name != "test-instance" {
t.Errorf("Expected name 'test-instance', got %q", inst.Name)
}
if !inst.Running {
t.Error("Expected running to be true")
if !inst.IsRunning() {
t.Error("Expected status to be running")
}
opts := inst.GetOptions()

View File

@@ -11,18 +11,12 @@ import (
"time"
)
func (i *Process) IsRunning() bool {
i.mu.RLock()
defer i.mu.RUnlock()
return i.Running
}
// Start starts the llama server instance and returns an error if it fails.
func (i *Process) Start() error {
i.mu.Lock()
defer i.mu.Unlock()
if i.Running {
if i.IsRunning() {
return fmt.Errorf("instance %s is already running", i.Name)
}
@@ -71,7 +65,7 @@ func (i *Process) Start() error {
return fmt.Errorf("failed to start instance %s: %w", i.Name, err)
}
i.Running = true
i.SetStatus(Running)
// Create channel for monitor completion signaling
i.monitorDone = make(chan struct{})
@@ -88,7 +82,7 @@ func (i *Process) Start() error {
func (i *Process) Stop() error {
i.mu.Lock()
if !i.Running {
if !i.IsRunning() {
// Even if not running, cancel any pending restart
if i.restartCancel != nil {
i.restartCancel()
@@ -105,8 +99,8 @@ func (i *Process) Stop() error {
i.restartCancel = nil
}
// Set running to false first to signal intentional stop
i.Running = false
// Set status to stopped first to signal intentional stop
i.SetStatus(Stopped)
// Clean up the proxy
i.proxy = nil
@@ -151,7 +145,7 @@ func (i *Process) Stop() error {
}
func (i *Process) WaitForHealthy(timeout int) error {
if !i.Running {
if !i.IsRunning() {
return fmt.Errorf("instance %s is not running", i.Name)
}
@@ -233,12 +227,12 @@ func (i *Process) monitorProcess() {
i.mu.Lock()
// Check if the instance was intentionally stopped
if !i.Running {
if !i.IsRunning() {
i.mu.Unlock()
return
}
i.Running = false
i.SetStatus(Stopped)
i.logger.Close()
// Cancel any existing restart context since we're handling a new exit

72
pkg/instance/status.go Normal file
View File

@@ -0,0 +1,72 @@
package instance
import (
"encoding/json"
"log"
)
// Enum for instance status
type InstanceStatus int
const (
Stopped InstanceStatus = iota
Running
Failed
)
var nameToStatus = map[string]InstanceStatus{
"stopped": Stopped,
"running": Running,
"failed": Failed,
}
var statusToName = map[InstanceStatus]string{
Stopped: "stopped",
Running: "running",
Failed: "failed",
}
func (p *Process) SetStatus(status InstanceStatus) {
p.mu.Lock()
defer p.mu.Unlock()
p.Status = status
}
func (p *Process) GetStatus() InstanceStatus {
p.mu.RLock()
defer p.mu.RUnlock()
return p.Status
}
// IsRunning returns true if the status is Running
func (p *Process) IsRunning() bool {
p.mu.RLock()
defer p.mu.RUnlock()
return p.Status == Running
}
func (s InstanceStatus) MarshalJSON() ([]byte, error) {
name, ok := statusToName[s]
if !ok {
name = "stopped" // Default to "stopped" for unknown status
}
return json.Marshal(name)
}
// UnmarshalJSON implements json.Unmarshaler
func (s *InstanceStatus) UnmarshalJSON(data []byte) error {
var str string
if err := json.Unmarshal(data, &str); err != nil {
return err
}
status, ok := nameToStatus[str]
if !ok {
log.Printf("Unknown instance status: %s", str)
status = Stopped // Default to Stopped on unknown status
}
*s = status
return nil
}

View File

@@ -13,7 +13,7 @@ func (i *Process) ShouldTimeout() bool {
i.mu.RLock()
defer i.mu.RUnlock()
if !i.Running || i.options.IdleTimeout == nil || *i.options.IdleTimeout <= 0 {
if !i.IsRunning() || i.options.IdleTimeout == nil || *i.options.IdleTimeout <= 0 {
return false
}

View File

@@ -94,7 +94,7 @@ func TestShouldTimeout_NoTimeoutConfigured(t *testing.T) {
inst := instance.NewInstance("test-instance", globalSettings, options)
// Simulate running state
inst.Running = true
inst.SetStatus(instance.Running)
if inst.ShouldTimeout() {
t.Errorf("Instance with %s should not timeout", tt.name)
@@ -117,7 +117,7 @@ func TestShouldTimeout_WithinTimeLimit(t *testing.T) {
}
inst := instance.NewInstance("test-instance", globalSettings, options)
inst.Running = true
inst.SetStatus(instance.Running)
// Update last request time to now
inst.UpdateLastRequestTime()
@@ -142,7 +142,7 @@ func TestShouldTimeout_ExceedsTimeLimit(t *testing.T) {
}
inst := instance.NewInstance("test-instance", globalSettings, options)
inst.Running = true
inst.SetStatus(instance.Running)
// Use MockTimeProvider to simulate old last request time
mockTime := NewMockTimeProvider(time.Now())