orchard/internal/worker/vmmanager/vm.go

110 lines
2.0 KiB
Go

package vmmanager
import (
"context"
"fmt"
"github.com/cirruslabs/orchard/pkg/resource/v1"
"go.uber.org/zap"
"strconv"
"sync"
)
type VM struct {
id string
Resource v1.VM
ctx context.Context
cancel context.CancelFunc
wg *sync.WaitGroup
}
func NewVM(vmResource v1.VM, logger *zap.SugaredLogger) *VM {
ctx, cancel := context.WithCancel(context.Background())
vm := &VM{
id: fmt.Sprintf("orchard-%s-%s", vmResource.Name, vmResource.UID),
Resource: vmResource,
ctx: ctx,
cancel: cancel,
wg: &sync.WaitGroup{},
}
vm.wg.Add(1)
go func() {
defer vm.wg.Done()
// Optimistic set the status to running. Will be synced later by the worker loop.
vm.Resource.Status = v1.VMStatusRunning
if err := vm.run(vm.ctx); err != nil {
logger.Errorf("VM %s failed: %v", vm.id, err)
vm.Resource.Status = v1.VMStatusFailed
} else {
vm.Resource.Status = v1.VMStatusStopped
}
}()
return vm
}
func (vm *VM) run(ctx context.Context) error {
_, _, err := Tart(ctx, "clone", vm.Resource.Image, vm.id)
if err != nil {
return err
}
if vm.Resource.Memory != 0 {
_, _, err = Tart(ctx, "set", "--memory", strconv.FormatUint(vm.Resource.Memory, 10), vm.id)
if err != nil {
return err
}
}
if vm.Resource.CPU != 0 {
_, _, err = Tart(ctx, "set", "--cpu", strconv.FormatUint(vm.Resource.CPU, 10), vm.id)
if err != nil {
return err
}
}
var runArgs = []string{"run"}
if vm.Resource.Softnet {
runArgs = append(runArgs, "--net-softnet")
}
if vm.Resource.Headless {
runArgs = append(runArgs, "--no-graphics")
}
runArgs = append(runArgs, vm.id)
_, _, err = Tart(ctx, runArgs...)
if err != nil {
return err
}
return nil
}
func (vm *VM) Stop() error {
_, _, _ = Tart(context.Background(), "stop", vm.id)
vm.cancel()
vm.wg.Wait()
return nil
}
func (vm *VM) Delete() error {
_, _, err := Tart(context.Background(), "delete", vm.id)
if err != nil {
return fmt.Errorf("%w: failed to delete VM %s: %v", ErrFailed, vm.id, err)
}
return nil
}