//go:build linux package microvm import ( "fmt" "os/exec" "runtime" "tangled.org/core/spindle/engines/microvm/placement" "tangled.org/core/spindle/models" ) func (e *Engine) ValidateWorkflowPlacement(wf *models.Workflow) error { state, ok := wf.Data.(*workflowState) if !ok || state == nil { return fmt.Errorf("microVM workflow state is not initialized") } return e.validateImagePlacement(state.ImageSpec) } func (e *Engine) validateImagePlacement(spec ImageSpec) error { if err := spec.Validate(); err != nil { return err } if !placement.IsNativeArchitecture(spec.Arch, runtime.GOARCH) { return fmt.Errorf("microVM image architecture %q is not native to executor architecture %q", spec.Arch, runtime.GOARCH) } if err := spec.validateImageFiles(); err != nil { return err } runner, err := runnerFor(spec.RunnerType) if err != nil { return err } if err := runner.Validate(spec, e.cfg.MicroVMPipelines.EnableKVM); err != nil { return err } if len(spec.Volumes) > 0 { if _, err := exec.LookPath("mkfs.ext4"); err != nil { return fmt.Errorf("required host command %q not found in PATH: %w", "mkfs.ext4", err) } for _, volume := range spec.Volumes { if volume.ReadOnly { return fmt.Errorf("read-only microvm volume %q is not supported yet", volume.Image) } if volume.FSType != "ext4" { return fmt.Errorf("microvm volume %q uses unsupported fsType %q", volume.Image, volume.FSType) } if volume.ImageType != "" && volume.ImageType != "raw" { return fmt.Errorf("microvm volume %q uses unsupported imageType %q", volume.Image, volume.ImageType) } } } request := resourcesForImage(spec) if !request.Fits(e.budget) || !request.Fits(e.maxWorkflow) { return fmt.Errorf("microVM image resources exceed executor limits: request=%v budget=%v max=%v", request, e.budget, e.maxWorkflow) } return nil }