Files
dobotai-platform/internal/runtime/manager.go
2026-08-13 13:36:26 +08:00

103 lines
2.7 KiB
Go

package runtime
import (
"context"
"errors"
"sync"
"time"
)
type Credentials struct {
ClientID string
Secret string
Ref string
}
type Component interface {
Start(context.Context, Credentials) error
Stop(context.Context) error
}
type Status struct {
State string
ClientID string
LastCode string
UpdatedAt time.Time
}
type Manager struct {
mu sync.Mutex
component Component
active Credentials
status Status
}
func NewManager(component Component) *Manager {
return &Manager{component: component, status: Status{State: "stopped", UpdatedAt: time.Now()}}
}
func (m *Manager) Start(ctx context.Context, credentials Credentials) error {
m.mu.Lock()
defer m.mu.Unlock()
if m.component == nil {
return errors.New("runtime_component_required")
}
if m.status.State == "running" {
return errors.New("runtime_already_running")
}
if err := m.component.Start(ctx, credentials); err != nil {
m.status = Status{State: "error", LastCode: "active_start_failed", UpdatedAt: time.Now()}
return errors.New("active_start_failed")
}
m.active = credentials
m.status = Status{State: "running", ClientID: credentials.ClientID, UpdatedAt: time.Now()}
return nil
}
func (m *Manager) Switch(ctx context.Context, candidate Credentials) error {
m.mu.Lock()
defer m.mu.Unlock()
if m.component == nil {
return errors.New("runtime_component_required")
}
old := m.active
m.status = Status{State: "switching", ClientID: old.ClientID, UpdatedAt: time.Now()}
if err := m.component.Stop(ctx); err != nil {
m.status = Status{State: "running", ClientID: old.ClientID, LastCode: "active_stop_failed", UpdatedAt: time.Now()}
return errors.New("active_stop_failed")
}
if err := m.component.Start(ctx, candidate); err == nil {
m.active = candidate
m.status = Status{State: "running", ClientID: candidate.ClientID, LastCode: "switch_ok", UpdatedAt: time.Now()}
return nil
}
if old.ClientID != "" && m.component.Start(ctx, old) == nil {
m.active = old
m.status = Status{State: "running", ClientID: old.ClientID, LastCode: "candidate_start_failed_rolled_back", UpdatedAt: time.Now()}
return errors.New("candidate_start_failed_rolled_back")
}
m.active = Credentials{}
m.status = Status{State: "error", LastCode: "rollback_failed", UpdatedAt: time.Now()}
return errors.New("rollback_failed")
}
func (m *Manager) Stop(ctx context.Context) error {
m.mu.Lock()
defer m.mu.Unlock()
if m.component == nil || m.status.State == "stopped" {
return nil
}
if err := m.component.Stop(ctx); err != nil {
return errors.New("runtime_stop_failed")
}
m.active = Credentials{}
m.status = Status{State: "stopped", UpdatedAt: time.Now()}
return nil
}
func (m *Manager) Status() Status {
m.mu.Lock()
defer m.mu.Unlock()
return m.status
}