create vm: introduce --{os,arch,runtime} command-line arguments (#422)

* create vm: introduce --{os,arch,runtime} command-line arguments

* v1.VM: prevent unsupported fields for "vetu" runtime
This commit is contained in:
Nikolay Edigaryev 2026-03-17 19:46:00 +01:00 committed by GitHub
parent 143253083b
commit 3cfa244550
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 68 additions and 2 deletions

View File

@ -16,6 +16,9 @@ import (
var ErrVMFailed = errors.New("failed to create VM")
var image string
var vmOSRaw string
var vmArchRaw string
var vmRuntimeRaw string
var cpu uint64
var memory uint64
var diskSize uint64
@ -45,6 +48,14 @@ func newCreateVMCommand() *cobra.Command {
}
command.Flags().StringVar(&image, "image", imageconstant.DefaultMacosImage, "image to use")
command.Flags().StringVar(&vmOSRaw, "os", string(v1.OSDarwin), fmt.Sprintf("operating system of this "+
"VM: %q or %q; set to \"linux\" to work around the Apple's limitation of 2 macOS VMs per host when using "+
"\"tart\" runtime", v1.OSDarwin, v1.OSLinux))
command.Flags().StringVar(&vmArchRaw, "arch", string(v1.ArchitectureARM64), fmt.Sprintf("architecture "+
"of this VM: %q or %q; ensures the VM is scheduled on an architecture-compatible machine in mixed-architecture "+
"clusters", v1.ArchitectureARM64, v1.ArchitectureAMD64))
command.Flags().StringVar(&vmRuntimeRaw, "runtime", string(v1.RuntimeTart), fmt.Sprintf("runtime to use "+
"for this VM: %q or %q; ensures the VM is scheduled on a runtime-compatible node", v1.RuntimeTart, v1.RuntimeVetu))
command.Flags().Uint64Var(&cpu, "cpu", 4, "number of CPUs to use")
command.Flags().Uint64Var(&memory, "memory", 8*1024, "megabytes of memory to use")
command.Flags().Uint64Var(&diskSize, "disk-size", 0, "resize the VMs disk to the specified size in GB "+
@ -100,6 +111,21 @@ func runCreateVM(cmd *cobra.Command, args []string) error {
// Convert arguments
var hostDirs []v1.HostDir
vmOS, err := v1.NewOSFromString(vmOSRaw)
if err != nil {
return fmt.Errorf("%w: %v", ErrVMFailed, err)
}
vmArch, err := v1.NewArchitectureFromString(vmArchRaw)
if err != nil {
return fmt.Errorf("%w: %v", ErrVMFailed, err)
}
vmRuntime, err := v1.NewRuntimeFromString(vmRuntimeRaw)
if err != nil {
return fmt.Errorf("%w: %v", ErrVMFailed, err)
}
for _, hostDirRaw := range hostDirsRaw {
hostDir, err := v1.NewHostDirFromString(hostDirRaw)
if err != nil {
@ -118,6 +144,9 @@ func runCreateVM(cmd *cobra.Command, args []string) error {
Memory: memory,
DiskSize: diskSize,
VMSpec: v1.VMSpec{
OS: vmOS,
Arch: vmArch,
Runtime: vmRuntime,
NetSoftnetDeprecated: netSoftnet,
NetSoftnet: netSoftnet,
NetSoftnetAllow: netSoftnetAllow,
@ -134,9 +163,11 @@ func runCreateVM(cmd *cobra.Command, args []string) error {
HostDirs: hostDirs,
}
// Convert resources
var err error
if err := vm.Validate(); err != nil {
return fmt.Errorf("%w: %v", ErrVMFailed, err)
}
// Convert resources
vm.Resources, err = v1.NewResourcesFromStringToString(resources)
if err != nil {
return fmt.Errorf("%w: %v", ErrVMFailed, err)

View File

@ -73,6 +73,10 @@ func (controller *Controller) createVM(ctx *gin.Context) responder.Responder {
vm.Runtime = v1.RuntimeTart
}
if err := vm.Validate(); err != nil {
return responder.JSON(http.StatusPreconditionFailed, NewErrorResponse("%v", err))
}
// Softnet-specific logic: automatically enable Softnet when NetSoftnetAllow or NetSoftnetBlock are set
// and propagate deprecated and non-deprecated boolean fields into each other
if vm.NetSoftnetDeprecated || vm.NetSoftnet || len(vm.NetSoftnetAllow) != 0 || len(vm.NetSoftnetBlock) != 0 {
@ -173,6 +177,10 @@ func (controller *Controller) updateVMSpec(ctx *gin.Context) responder.Responder
"and \"runtime\" fields cannot be modified"))
}
if err := userVM.Validate(); err != nil {
return responder.JSON(http.StatusPreconditionFailed, NewErrorResponse("%v", err))
}
// Softnet-specific logic: automatically enable Softnet when NetSoftnetAllow or NetSoftnetBlock are set
// and propagate deprecated and non-deprecated boolean fields into each other
if userVM.NetSoftnetDeprecated || userVM.NetSoftnet || len(userVM.NetSoftnetAllow) != 0 || len(userVM.NetSoftnetBlock) != 0 {

View File

@ -137,6 +137,33 @@ func (vm *VM) IsScheduled() bool {
}
}
func (vm *VM) Validate() error {
unsupportedFieldError := func(field string) error {
return fmt.Errorf("runtime %q does not support field %q", vm.Runtime, field)
}
switch vm.Runtime {
case RuntimeVetu:
if vm.NetSoftnetDeprecated || vm.NetSoftnet {
return unsupportedFieldError("netSoftnet")
}
if len(vm.NetSoftnetAllow) != 0 {
return unsupportedFieldError("netSoftnetAllow")
}
if len(vm.NetSoftnetBlock) != 0 {
return unsupportedFieldError("netSoftnetBlock")
}
if len(vm.HostDirs) != 0 {
return unsupportedFieldError("hostDirs")
}
if vm.Suspendable {
return unsupportedFieldError("suspendable")
}
}
return nil
}
type VMSpec struct {
// OS defines the operating system used by a VM.
//