Skip to content

Commit 751f5b6

Browse files
authored
Merge pull request #709 from chaitin/fix/vm-preinsert
fix: 创建虚拟机前先写入本地记录
2 parents 5fd15fa + 9d76403 commit 751f5b6

16 files changed

Lines changed: 1081 additions & 192 deletions

File tree

backend/biz/host/handler/v1/internal_auth_test.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,14 @@ func (s *internalHostRepoStub) CreateVirtualMachine(context.Context, *domain.Use
255255
return nil, errors.New("not implemented")
256256
}
257257

258+
func (s *internalHostRepoStub) PrepareCreateVirtualMachine(context.Context, *domain.User, *domain.CreateVMReq, string) (*domain.PreparedVirtualMachine, error) {
259+
return nil, errors.New("not implemented")
260+
}
261+
262+
func (s *internalHostRepoStub) CompleteCreateVirtualMachine(context.Context, string, *taskflow.VirtualMachine) (*domain.VirtualMachine, error) {
263+
return nil, errors.New("not implemented")
264+
}
265+
258266
func (s *internalHostRepoStub) PastHourVirtualMachine(context.Context) ([]*db.VirtualMachine, error) {
259267
return nil, errors.New("not implemented")
260268
}

backend/biz/host/repo/host.go

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -443,6 +443,169 @@ func (h *HostRepo) CreateVirtualMachine(ctx context.Context, u *domain.User, req
443443
return res, err
444444
}
445445

446+
func (h *HostRepo) PrepareCreateVirtualMachine(ctx context.Context, u *domain.User, req *domain.CreateVMReq, id string) (*domain.PreparedVirtualMachine, error) {
447+
var res *domain.PreparedVirtualMachine
448+
err := entx.WithTx2(ctx, h.db, func(tx *db.Tx) error {
449+
dbHost, err := tx.Host.Query().
450+
WithUser().
451+
WithGroups().
452+
Where(hostWithUserPredicate(u.ID)).
453+
Where(host.ID(req.HostID)).
454+
First(ctx)
455+
if err != nil {
456+
return errcode.ErrPermision.Wrap(err)
457+
}
458+
459+
if u := dbHost.Edges.User; u != nil && u.Role == consts.UserRoleAdmin {
460+
if req.Life > 3*60*60 || req.Life <= 0 {
461+
return errcode.ErrPublicHostBeyondLimit
462+
}
463+
}
464+
465+
if len(dbHost.Edges.Groups) > 0 && (req.Life <= 0 || req.Life > 7*24*60*60) {
466+
return errcode.ErrVmBeyondExpireTime.Wrap(fmt.Errorf("团队宿主机不支持创建永久虚拟机"))
467+
}
468+
469+
if req.UsePublicHost && h.cfg.PublicHost.CountLimit > 0 {
470+
cnt, err := tx.VirtualMachine.Query().
471+
Where(virtualmachine.UserID(u.ID)).
472+
Where(virtualmachine.HasHostWith(host.HasUserWith(user.Role(consts.UserRoleAdmin)))).
473+
Where(virtualmachine.ExpiredAtGT(time.Now())).
474+
Count(ctx)
475+
if err != nil {
476+
return errcode.ErrDatabaseOperation.Wrap(err)
477+
}
478+
if cnt >= h.cfg.PublicHost.CountLimit {
479+
return errcode.ErrPublicHostBeyondLimit.Wrap(fmt.Errorf("public host limit reached: %d", h.cfg.PublicHost.CountLimit))
480+
}
481+
}
482+
483+
var m *db.Model
484+
if len(req.ModelID) > 0 {
485+
switch req.ModelID {
486+
case "economy":
487+
m, err = tx.Model.Query().
488+
WithPricing().
489+
WithUser().
490+
Where(model.HasUserWith(user.Role(consts.UserRoleAdmin))).
491+
Where(model.Remark(req.ModelID)).
492+
First(ctx)
493+
if err != nil {
494+
return err
495+
}
496+
default:
497+
mid, err := uuid.Parse(req.ModelID)
498+
if err != nil {
499+
return err
500+
}
501+
m, err = tx.Model.Query().WithPricing().WithUser().Where(model.ID(mid)).First(ctx)
502+
if err != nil {
503+
return err
504+
}
505+
}
506+
507+
if m != nil {
508+
if p := m.Edges.Pricing; p != nil {
509+
apikey := uuid.NewString()
510+
mak, err := tx.ModelApiKey.Create().
511+
SetAPIKey(apikey).
512+
SetUserID(u.ID).
513+
SetModelID(m.ID).
514+
SetVirtualmachineID(id).
515+
Save(ctx)
516+
if err != nil {
517+
return err
518+
}
519+
m.Edges.Apikeys = append(m.Edges.Apikeys, mak)
520+
}
521+
}
522+
}
523+
524+
repoURL := ""
525+
repoFilename := ""
526+
branch := ""
527+
if req.RepoReq != nil {
528+
repoURL = req.RepoReq.RepoURL
529+
repoFilename = req.RepoReq.RepoFilename
530+
branch = req.RepoReq.Branch
531+
}
532+
533+
img, err := tx.Image.Get(ctx, req.ImageID)
534+
if err != nil {
535+
return err
536+
}
537+
538+
var expiredAt *time.Time
539+
if req.Life > 0 {
540+
t := req.Now.Add(time.Duration(req.Life) * time.Second)
541+
expiredAt = &t
542+
}
543+
544+
crt := tx.VirtualMachine.Create().
545+
SetID(id).
546+
SetUserID(u.ID).
547+
SetName(req.Name).
548+
SetHostID(dbHost.ID).
549+
SetCores(req.Resource.CPU).
550+
SetMemory(req.Resource.Memory).
551+
SetRepoURL(repoURL).
552+
SetRepoFilename(repoFilename).
553+
SetBranch(branch).
554+
SetCreatedAt(req.Now)
555+
if expiredAt != nil {
556+
crt.SetExpiredAt(*expiredAt)
557+
}
558+
if len(req.ModelID) > 0 {
559+
crt.SetModelID(m.ID)
560+
}
561+
if req.GitIdentityID != uuid.Nil {
562+
crt.SetGitIdentityID(req.GitIdentityID)
563+
}
564+
if err := crt.Exec(ctx); err != nil {
565+
return err
566+
}
567+
568+
res = &domain.PreparedVirtualMachine{
569+
VirtualMachine: &domain.VirtualMachine{
570+
ID: id,
571+
Name: req.Name,
572+
LifeTimeSeconds: req.Life,
573+
Host: cvt.From(dbHost, &domain.Host{}),
574+
},
575+
Model: m,
576+
Image: img,
577+
}
578+
return nil
579+
})
580+
return res, err
581+
}
582+
583+
func (h *HostRepo) CompleteCreateVirtualMachine(ctx context.Context, id string, vm *taskflow.VirtualMachine) (*domain.VirtualMachine, error) {
584+
var res *db.VirtualMachine
585+
err := entx.WithTx2(ctx, h.db, func(tx *db.Tx) error {
586+
up := tx.VirtualMachine.UpdateOneID(id).
587+
SetEnvironmentID(vm.EnvironmentID)
588+
if vm.AccessToken != "" {
589+
up.SetAccessToken(vm.AccessToken)
590+
}
591+
if err := up.Exec(ctx); err != nil {
592+
return err
593+
}
594+
595+
var err error
596+
res, err = tx.VirtualMachine.Query().
597+
WithHost(func(hq *db.HostQuery) { hq.WithUser() }).
598+
WithUser().
599+
Where(virtualmachine.ID(id)).
600+
First(ctx)
601+
return err
602+
})
603+
if err != nil {
604+
return nil, err
605+
}
606+
return cvt.From(res, &domain.VirtualMachine{}), nil
607+
}
608+
446609
// DeleteVirtualMachine implements domain.HostRepo.
447610
func (h *HostRepo) DeleteVirtualMachine(ctx context.Context, uid uuid.UUID, hostID, id string, fn func(*db.VirtualMachine) error) error {
448611
return entx.WithTx2(ctx, h.db, func(tx *db.Tx) error {

backend/biz/host/repo/host_delete_test.go

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
"github.com/chaitin/MonkeyCode/backend/db"
1313
"github.com/chaitin/MonkeyCode/backend/db/enttest"
1414
"github.com/chaitin/MonkeyCode/backend/db/virtualmachine"
15+
"github.com/chaitin/MonkeyCode/backend/domain"
1516
"github.com/chaitin/MonkeyCode/backend/pkg/entx"
1617
)
1718

@@ -152,6 +153,69 @@ func TestHostRepo_CountDownQueriesUseExpiredAt(t *testing.T) {
152153
}
153154
}
154155

156+
func TestHostRepo_PrepareCreateVirtualMachinePersistsBeforeTaskflowCreate(t *testing.T) {
157+
t.Parallel()
158+
159+
ctx := context.Background()
160+
client := enttest.Open(t, "sqlite3", "file:host-prepare-create-vm-test?mode=memory&cache=shared&_fk=1")
161+
defer client.Close()
162+
163+
repo := &HostRepo{db: client}
164+
uid := uuid.New()
165+
imageID := uuid.New()
166+
now := time.Now()
167+
168+
if _, err := client.User.Create().
169+
SetID(uid).
170+
SetName("tester").
171+
SetRole(consts.UserRoleIndividual).
172+
SetStatus(consts.UserStatusActive).
173+
Save(ctx); err != nil {
174+
t.Fatalf("create user: %v", err)
175+
}
176+
if _, err := client.Host.Create().
177+
SetID("host-1").
178+
SetUserID(uid).
179+
SetHostname("host").
180+
Save(ctx); err != nil {
181+
t.Fatalf("create host: %v", err)
182+
}
183+
if _, err := client.Image.Create().
184+
SetID(imageID).
185+
SetUserID(uid).
186+
SetName("image").
187+
Save(ctx); err != nil {
188+
t.Fatalf("create image: %v", err)
189+
}
190+
191+
prepared, err := repo.PrepareCreateVirtualMachine(ctx, &domain.User{ID: uid}, &domain.CreateVMReq{
192+
HostID: "host-1",
193+
ImageID: imageID,
194+
Name: "manual-vm",
195+
Resource: &domain.Resource{
196+
CPU: 2,
197+
Memory: 4096,
198+
},
199+
Now: now,
200+
}, "agent_preallocated")
201+
if err != nil {
202+
t.Fatalf("prepare create virtual machine: %v", err)
203+
}
204+
if prepared.VirtualMachine.ID != "agent_preallocated" {
205+
t.Fatalf("prepared vm id = %q, want agent_preallocated", prepared.VirtualMachine.ID)
206+
}
207+
208+
vm, err := client.VirtualMachine.Query().
209+
Where(virtualmachine.ID("agent_preallocated")).
210+
Only(ctx)
211+
if err != nil {
212+
t.Fatalf("query prepared vm before taskflow create: %v", err)
213+
}
214+
if vm.EnvironmentID != "" {
215+
t.Fatalf("prepared vm environment_id = %q, want empty before taskflow create", vm.EnvironmentID)
216+
}
217+
}
218+
155219
func vmIDs(vms []*db.VirtualMachine) []string {
156220
ids := make([]string, 0, len(vms))
157221
for _, vm := range vms {

0 commit comments

Comments
 (0)