Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 15 additions & 15 deletions backend/biz/host/usecase/host.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import (
"github.com/chaitin/MonkeyCode/backend/pkg/entx"
"github.com/chaitin/MonkeyCode/backend/pkg/random"
"github.com/chaitin/MonkeyCode/backend/pkg/taskflow"
"github.com/chaitin/MonkeyCode/backend/pkg/vmstatus"
"github.com/chaitin/MonkeyCode/backend/templates"
)

Expand Down Expand Up @@ -225,12 +226,14 @@ func (h *HostUsecase) List(ctx context.Context, uid uuid.UUID) (*domain.HostList
dHost := cvt.From(host, &domain.Host{Status: status})
dHost.IsDefault = dHost.GetIsDefault(user)
dHost.VirtualMachines = cvt.Iter(host.Edges.Vms, func(_ int, vm *db.VirtualMachine) *domain.VirtualMachine {
status := taskflow.VirtualMachineStatusOffline
if vmonline.OnlineMap[vm.ID] {
status = taskflow.VirtualMachineStatusOnline
}
return cvt.From(vm, &domain.VirtualMachine{
Status: status,
Status: vmstatus.Resolve(vmstatus.Input{
Online: vmonline.OnlineMap[vm.ID],
Conditions: vm.Conditions.Conditions,
IsRecycled: vm.IsRecycled,
CreatedAt: vm.CreatedAt,
Now: time.Now(),
}),
})
})

Expand Down Expand Up @@ -476,17 +479,14 @@ func (h *HostUsecase) VMInfo(ctx context.Context, uid uuid.UUID, id string) (*do
return nil, err
}

status := taskflow.VirtualMachineStatusOffline
if info, err := h.taskflow.VirtualMachiner().Info(ctx, taskflow.VirtualMachineInfoReq{
ID: vm.ID,
UserID: vm.UserID.String(),
}); err == nil && info != nil && info.Status != taskflow.VirtualMachineStatusUnknown {
status = info.Status
} else if vmonline.OnlineMap[vm.ID] {
status = taskflow.VirtualMachineStatusOnline
}
dvm := cvt.From(vm, &domain.VirtualMachine{
Status: status,
Status: vmstatus.Resolve(vmstatus.Input{
Online: vmonline.OnlineMap[vm.ID],
Conditions: vm.Conditions.Conditions,
IsRecycled: vm.IsRecycled,
CreatedAt: vm.CreatedAt,
Now: time.Now(),
}),
})

if dvm.Host != nil {
Expand Down
29 changes: 7 additions & 22 deletions backend/biz/task/usecase/task.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,14 @@ import (
"github.com/chaitin/MonkeyCode/backend/consts"
"github.com/chaitin/MonkeyCode/backend/db"
"github.com/chaitin/MonkeyCode/backend/domain"
"github.com/chaitin/MonkeyCode/backend/ent/types"
"github.com/chaitin/MonkeyCode/backend/errcode"
"github.com/chaitin/MonkeyCode/backend/pkg/cvt"
"github.com/chaitin/MonkeyCode/backend/pkg/entx"
"github.com/chaitin/MonkeyCode/backend/pkg/lifecycle"
"github.com/chaitin/MonkeyCode/backend/pkg/loki"
"github.com/chaitin/MonkeyCode/backend/pkg/notify/dispatcher"
"github.com/chaitin/MonkeyCode/backend/pkg/taskflow"
"github.com/chaitin/MonkeyCode/backend/pkg/vmstatus"
"github.com/chaitin/MonkeyCode/backend/templates"
)

Expand Down Expand Up @@ -128,27 +128,12 @@ func (a *TaskUsecase) Info(ctx context.Context, user *domain.User, id uuid.UUID)
IDs: []string{vm.ID},
})
a.logger.With("resp", resp, "id", vm.ID).DebugContext(ctx, "is online check")

if resp != nil && resp.OnlineMap[vm.ID] {
vm.Status = taskflow.VirtualMachineStatusOnline
} else {
vm.Status = taskflow.VirtualMachineStatusPending

for _, cond := range vm.Conditions {
switch cond.Type {
case types.ConditionTypeFailed:
vm.Status = taskflow.VirtualMachineStatusOffline
case types.ConditionTypeHibernated:
if strings.ToLower(strings.TrimSpace(cond.Reason)) == "hibernated" {
vm.Status = taskflow.VirtualMachineStatusHibernated
}
case types.ConditionTypeReady:
if time.Since(time.Unix(vm.CreatedAt, 0)) > 2*time.Minute {
vm.Status = taskflow.VirtualMachineStatusOffline
}
}
}
}
vm.Status = vmstatus.Resolve(vmstatus.Input{
Online: resp != nil && resp.OnlineMap[vm.ID],
Conditions: vm.Conditions,
CreatedAt: time.Unix(vm.CreatedAt, 0),
Now: time.Now(),
})
}

if stat, err := a.repo.Stat(ctx, id); err == nil {
Expand Down
13 changes: 8 additions & 5 deletions backend/biz/team/usecase/host.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"github.com/chaitin/MonkeyCode/backend/domain"
"github.com/chaitin/MonkeyCode/backend/pkg/cvt"
"github.com/chaitin/MonkeyCode/backend/pkg/taskflow"
"github.com/chaitin/MonkeyCode/backend/pkg/vmstatus"
)

// TeamHostUsecase 团队宿主机业务逻辑层
Expand Down Expand Up @@ -103,12 +104,14 @@ func (u *TeamHostUsecase) List(ctx context.Context, teamUser *domain.TeamUser) (
})

dHost.VirtualMachines = cvt.Iter(host.Edges.Vms, func(_ int, vm *db.VirtualMachine) *domain.VirtualMachine {
status := taskflow.VirtualMachineStatusOffline
if vmonline.OnlineMap[vm.ID] {
status = taskflow.VirtualMachineStatusOnline
}
return cvt.From(vm, &domain.VirtualMachine{
Status: status,
Status: vmstatus.Resolve(vmstatus.Input{
Online: vmonline.OnlineMap[vm.ID],
Conditions: vm.Conditions.Conditions,
IsRecycled: vm.IsRecycled,
CreatedAt: vm.CreatedAt,
Now: time.Now(),
}),
})
})

Expand Down
28 changes: 8 additions & 20 deletions backend/domain/host.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
etypes "github.com/chaitin/MonkeyCode/backend/ent/types"
"github.com/chaitin/MonkeyCode/backend/pkg/cvt"
"github.com/chaitin/MonkeyCode/backend/pkg/taskflow"
"github.com/chaitin/MonkeyCode/backend/pkg/vmstatus"
)

// HostUsecase 主机业务逻辑接口
Expand Down Expand Up @@ -167,29 +168,16 @@ func (v *VirtualMachine) From(vm *db.VirtualMachine) *VirtualMachine {
v.Version = vm.Version
v.EnvironmentID = vm.EnvironmentID
v.CreatedAt = vm.CreatedAt.Unix()

if vm.Conditions != nil {
v.Conditions = vm.Conditions.Conditions
if v.Status != taskflow.VirtualMachineStatusOnline {
v.Status = taskflow.VirtualMachineStatusPending
for _, cond := range vm.Conditions.Conditions {
switch cond.Type {
case etypes.ConditionTypeFailed:
v.Status = taskflow.VirtualMachineStatusOffline
case etypes.ConditionTypeHibernated:
v.Status = taskflow.VirtualMachineStatusHibernated
case etypes.ConditionTypeReady:
if time.Since(time.UnixMilli(cond.LastTransitionTime)) > 3*time.Minute {
v.Status = taskflow.VirtualMachineStatusOffline
}
}
}
}
}

if vm.IsRecycled {
v.Status = taskflow.VirtualMachineStatusOffline
}
v.Status = vmstatus.Resolve(vmstatus.Input{
Online: v.Status == taskflow.VirtualMachineStatusOnline,
Conditions: v.Conditions,
IsRecycled: vm.IsRecycled,
CreatedAt: vm.CreatedAt,
Now: time.Now(),
})

switch vm.TTLKind {
case consts.CountDown:
Expand Down
43 changes: 43 additions & 0 deletions backend/pkg/vmstatus/status.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package vmstatus

import (
"time"

etypes "github.com/chaitin/MonkeyCode/backend/ent/types"
"github.com/chaitin/MonkeyCode/backend/pkg/taskflow"
)

const readyTimeout = 3 * time.Minute

type Input struct {
Online bool
Conditions []*etypes.Condition
IsRecycled bool
CreatedAt time.Time
Now time.Time
}

func Resolve(input Input) taskflow.VirtualMachineStatus {
if input.IsRecycled {
return taskflow.VirtualMachineStatusOffline
}

if input.Online {
return taskflow.VirtualMachineStatusOnline
}

for _, cond := range input.Conditions {
switch cond.Type {
case etypes.ConditionTypeFailed:
return taskflow.VirtualMachineStatusOffline
case etypes.ConditionTypeHibernated:
return taskflow.VirtualMachineStatusHibernated
case etypes.ConditionTypeReady:
if !input.CreatedAt.IsZero() && input.Now.Sub(input.CreatedAt) > readyTimeout {
return taskflow.VirtualMachineStatusOffline
}
}
}

return taskflow.VirtualMachineStatusPending
}
119 changes: 119 additions & 0 deletions backend/pkg/vmstatus/status_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
package vmstatus

import (
"reflect"
"testing"
"time"

etypes "github.com/chaitin/MonkeyCode/backend/ent/types"
"github.com/chaitin/MonkeyCode/backend/pkg/taskflow"
)

func TestInputDoesNotExposeReportedStatus(t *testing.T) {
if _, ok := reflect.TypeOf(Input{}).FieldByName("ReportedStatus"); ok {
t.Fatal("Input should not expose ReportedStatus")
}
}

func TestResolve(t *testing.T) {
now := time.Date(2026, 4, 15, 12, 0, 0, 0, time.UTC)

tests := []struct {
name string
input Input
want taskflow.VirtualMachineStatus
}{
{
name: "is recycled overrides everything",
input: Input{
Online: true,
IsRecycled: true,
CreatedAt: now.Add(-10 * time.Minute),
Now: now,
},
want: taskflow.VirtualMachineStatusOffline,
},
{
name: "online returns online before conditions",
input: Input{
Online: true,
Conditions: []*etypes.Condition{
{Type: etypes.ConditionTypeFailed},
},
CreatedAt: now.Add(-10 * time.Minute),
Now: now,
},
want: taskflow.VirtualMachineStatusOnline,
},
{
name: "online returns online",
input: Input{
Online: true,
CreatedAt: now.Add(-10 * time.Minute),
Now: now,
},
want: taskflow.VirtualMachineStatusOnline,
},
{
name: "failed condition returns offline",
input: Input{
Conditions: []*etypes.Condition{
{Type: etypes.ConditionTypeFailed},
},
CreatedAt: now.Add(-10 * time.Minute),
Now: now,
},
want: taskflow.VirtualMachineStatusOffline,
},
{
name: "hibernated condition returns hibernated",
input: Input{
Conditions: []*etypes.Condition{
{Type: etypes.ConditionTypeHibernated},
},
CreatedAt: now.Add(-10 * time.Minute),
Now: now,
},
want: taskflow.VirtualMachineStatusHibernated,
},
{
name: "ready older than three minutes returns offline",
input: Input{
Conditions: []*etypes.Condition{
{Type: etypes.ConditionTypeReady},
},
CreatedAt: now.Add(-3*time.Minute - time.Second),
Now: now,
},
want: taskflow.VirtualMachineStatusOffline,
},
{
name: "ready within three minutes stays pending",
input: Input{
Conditions: []*etypes.Condition{
{Type: etypes.ConditionTypeReady},
},
CreatedAt: now.Add(-2 * time.Minute),
Now: now,
},
want: taskflow.VirtualMachineStatusPending,
},
{
name: "defaults to pending",
input: Input{
CreatedAt: now,
Now: now,
},
want: taskflow.VirtualMachineStatusPending,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := Resolve(tt.input)
if got != tt.want {
t.Fatalf("Resolve() = %q, want %q", got, tt.want)
}
})
}
}