Compare commits

..

2 Commits
0.56.1 ... main

Author SHA1 Message Date
edi-oai b217649ef0
Prevent failed host-dir VMs from restarting (#463) 2026-08-17 16:44:43 -07:00
edi-oai 3ec5e3e585
Disable host directory sharing by default (#462) 2026-08-17 22:45:08 +01:00
8 changed files with 104 additions and 4 deletions

View File

@ -9,7 +9,6 @@ formatters:
- gofmt
- gofumpt
- goimports
- golines
- swaggo
linters:
@ -41,6 +40,7 @@ linters:
# Style linters that are total nuts.
- wsl
- wsl_v5
- funlen
# Enough parallelism for now.
@ -70,6 +70,18 @@ linters:
# Not all errors need to be checked
- errcheck
# It's OK to not initialize some struct fields
- exhaustruct
# We'll control the variable name length ourselves
- varnamelen
# Inline error handling keeps assignment and checking together
- noinlineerr
# Avoid unrelated style churn for now
- funcorder
issues:
# Don't hide multiple issues that belong to one class since GitHub annotations can handle them all nicely.
max-issues-per-linter: 0

View File

@ -791,7 +791,9 @@ components:
model: macstudio
hostDirs:
type: array
description: Directories on the Orchard Worker host to mount to a VM
description: |
Directories on the Orchard Worker host to mount to a VM.
Requires running Orchard Controller with `--insecure-allow-host-dirs`.
items:
type: object
properties:

View File

@ -30,6 +30,7 @@ var addressPprof string
var debug bool
var noTLS bool
var sshNoClientAuth bool
var insecureAllowHostDirs bool
var experimentalRPCV2 bool
var noExperimentalRPCV2 bool
var experimentalPingInterval time.Duration
@ -74,6 +75,8 @@ func newRunCommand() *cobra.Command {
cmd.Flags().BoolVar(&sshNoClientAuth, "insecure-ssh-no-client-auth", false,
"allow SSH clients to connect to the controller's SSH server without authentication, "+
"thus only authenticating on the target worker/VM's SSH server")
cmd.Flags().BoolVar(&insecureAllowHostDirs, "insecure-allow-host-dirs", false,
"allow unsafe path-based local host directory sharing")
cmd.Flags().BoolVar(&experimentalRPCV2, "experimental-rpc-v2", false,
"enable experimental RPC v2 (https://github.com/cirruslabs/orchard/issues/235)")
_ = cmd.Flags().MarkHidden("experimental-rpc-v2")
@ -166,6 +169,10 @@ func runController(cmd *cobra.Command, args []string) (err error) {
controllerOpts = append(controllerOpts, controller.WithSynthetic())
}
if insecureAllowHostDirs {
controllerOpts = append(controllerOpts, controller.WithInsecureAllowHostDirs())
}
var controllerCert tls.Certificate
if !noTLS {

View File

@ -35,6 +35,7 @@ var experimentalRPCV2 bool
var addressPprof string
var synthetic bool
var workers int
var insecureAllowHostDirs bool
func NewCommand() *cobra.Command {
command := &cobra.Command{
@ -57,6 +58,8 @@ func NewCommand() *cobra.Command {
command.Flags().BoolVar(&synthetic, "synthetic", false,
"do not instantiate real Tart VM, use synthetic in-memory VMs suitable for load testing")
command.Flags().IntVar(&workers, "workers", 1, "number of workers to start")
command.Flags().BoolVar(&insecureAllowHostDirs, "insecure-allow-host-dirs", false,
"allow unsafe path-based local host directory sharing")
return command
}
@ -105,6 +108,10 @@ func runDev(cmd *cobra.Command, args []string) error {
additionalControllerOpts = append(additionalControllerOpts, controller.WithExperimentalRPCV2())
}
if insecureAllowHostDirs {
additionalControllerOpts = append(additionalControllerOpts, controller.WithInsecureAllowHostDirs())
}
group, ctx := errgroup.WithContext(cmd.Context())
var additionalWorkerOpts []worker.Option

View File

@ -158,6 +158,10 @@ func (controller *Controller) updateVMSpec(ctx *gin.Context) responder.Responder
return responder.JSON(http.StatusBadRequest, NewErrorResponse("invalid JSON was provided"))
}
if responder := controller.validateHostDirs(userVM.HostDirs); responder != nil {
return responder
}
name := ctx.Param("name")
return controller.storeUpdate(func(txn storepkg.Transaction) responder.Responder {
@ -540,6 +544,14 @@ func (controller *Controller) validateHostDirs(hostDirs []v1.HostDir) responder.
return nil
}
if !controller.insecureAllowHostDirs {
return responder.JSON(
http.StatusBadRequest,
NewErrorResponse("host directory sharing is disabled; "+
"restart the controller with --insecure-allow-host-dirs to enable this unsafe feature"),
)
}
// Retrieve cluster settings
var clusterSettings *v1.ClusterSettings
var err error

View File

@ -46,6 +46,7 @@ type Controller struct {
listener net.Listener
httpServer *http.Server
insecureAuthDisabled bool
insecureAllowHostDirs bool
scheduler *scheduler.Scheduler
store storepkg.Store
logger *zap.SugaredLogger
@ -195,6 +196,14 @@ func New(opts ...Option) (*Controller, error) {
return nil, err
}
// When no "--insecure-allow-host-dirs" is present,
// fail the VMs that have "hostDirs" set
if !controller.insecureAllowHostDirs {
if err := controller.failVMsWithHostDirs(); err != nil {
return nil, err
}
}
return controller, nil
}
@ -305,6 +314,34 @@ func (controller *Controller) DeleteServiceAccount(name string) error {
})
}
func (controller *Controller) failVMsWithHostDirs() error {
return controller.store.Update(func(txn storepkg.Transaction) error {
vms, err := txn.ListVMs()
if err != nil {
return err
}
for _, vm := range vms {
permanentlyFailed := vm.TerminalState() &&
vm.RestartPolicy == v1.RestartPolicyNever
if permanentlyFailed || len(vm.HostDirs) == 0 {
continue
}
vm.Status = v1.VMStatusFailed
vm.StatusMessage = "host directories are used, but host directory sharing is disabled"
vm.RestartPolicy = v1.RestartPolicyNever
if err := txn.SetVM(vm); err != nil {
return err
}
}
return nil
})
}
func (controller *Controller) Run(ctx context.Context) error {
// Run the scheduler so that each VM will eventually
// be assigned to a specific Worker

View File

@ -48,6 +48,12 @@ func WithInsecureAuthDisabled() Option {
}
}
func WithInsecureAllowHostDirs() Option {
return func(controller *Controller) {
controller.insecureAllowHostDirs = true
}
}
func WithSwaggerDocs() Option {
return func(controller *Controller) {
controller.enableSwaggerDocs = true

View File

@ -372,12 +372,26 @@ func TestVMGarbageCollection(t *testing.T) {
}), "failed to wait for the VM %s to be garbage-collected", vmName)
}
func TestHostDirsDisabledByDefault(t *testing.T) {
devClient, _, _ := devcontroller.StartIntegrationTestEnvironment(t)
err := devClient.VMs().Create(context.Background(), &v1.VM{
Meta: v1.Meta{Name: "test-host-dirs-disabled"},
Image: imageconstant.DefaultMacosImage,
HostDirs: []v1.HostDir{{Name: "src", Path: "/Users/ci/src"}},
})
require.Error(t, err)
}
func TestHostDirs(t *testing.T) {
if runtime.GOOS != "darwin" {
t.Skip("HostDirs is only supported on macOS with Tart")
}
devClient, _, _ := devcontroller.StartIntegrationTestEnvironment(t)
devClient, _, _ := devcontroller.StartIntegrationTestEnvironmentWithAdditionalOpts(t,
false, []controller.Option{controller.WithInsecureAllowHostDirs()},
false, nil,
)
dirToMount := t.TempDir()
@ -449,7 +463,10 @@ func TestHostDirsInvalidPolicy(t *testing.T) {
t.Skip("HostDirs is only supported on macOS with Tart")
}
devClient, _, _ := devcontroller.StartIntegrationTestEnvironment(t)
devClient, _, _ := devcontroller.StartIntegrationTestEnvironmentWithAdditionalOpts(t,
false, []controller.Option{controller.WithInsecureAllowHostDirs()},
false, nil,
)
dirToMount := t.TempDir()