Worker: introduce --experimental-vm-stop-timeout (#505)

This commit is contained in:
edi-oai
2026-09-24 18:01:08 +01:00
committed by GitHub
parent f716825279
commit f101ade5d8
9 changed files with 59 additions and 28 deletions
+5
View File
@@ -48,6 +48,7 @@ var defaultMemory uint64
var username string var username string
var addressPprof string var addressPprof string
var debug bool var debug bool
var experimentalVMStopTimeout uint16
// Hidden flags // Hidden flags
var synthetic bool var synthetic bool
@@ -88,6 +89,9 @@ func newRunCommand() *cobra.Command {
cmd.Flags().StringVar(&addressPprof, "listen-pprof", "", cmd.Flags().StringVar(&addressPprof, "listen-pprof", "",
"start pprof HTTP server on localhost:6060 for diagnostic purposes (e.g. \"localhost:6060\")") "start pprof HTTP server on localhost:6060 for diagnostic purposes (e.g. \"localhost:6060\")")
cmd.Flags().BoolVar(&debug, "debug", false, "enable debug logging") cmd.Flags().BoolVar(&debug, "debug", false, "enable debug logging")
cmd.Flags().Uint16Var(&experimentalVMStopTimeout, "experimental-vm-stop-timeout",
worker.DefaultVMStopTimeoutSeconds,
"seconds to wait for graceful shutdown of Tart and Vetu VMs before forceful termination")
// Hidden flags // Hidden flags
cmd.Flags().BoolVar(&synthetic, "synthetic", false, cmd.Flags().BoolVar(&synthetic, "synthetic", false,
@@ -107,6 +111,7 @@ func runWorker(cmd *cobra.Command, args []string) (err error) {
worker.WithName(name), worker.WithName(name),
worker.WithLabels(labels), worker.WithLabels(labels),
worker.WithDefaultCPUAndMemory(defaultCPU, defaultMemory), worker.WithDefaultCPUAndMemory(defaultCPU, defaultMemory),
worker.WithVMStopTimeout(experimentalVMStopTimeout),
} }
// Run the macOS "Local Network" permission helper // Run the macOS "Local Network" permission helper
+6
View File
@@ -59,6 +59,12 @@ func WithSoftnetPolicyUpdates(enabled bool) Option {
} }
} }
func WithVMStopTimeout(seconds uint16) Option {
return func(worker *Worker) {
worker.vmStopTimeoutSeconds = seconds
}
}
func WithLogger(logger *zap.Logger) Option { func WithLogger(logger *zap.Logger) Option {
return func(worker *Worker) { return func(worker *Worker) {
worker.logger = logger.Sugar() worker.logger = logger.Sugar()
+7 -4
View File
@@ -12,10 +12,12 @@ import (
"go.uber.org/zap" "go.uber.org/zap"
) )
type Tart struct{} type Tart struct {
stopTimeoutSeconds uint16
}
func NewTart() *Tart { func NewTart(stopTimeoutSeconds uint16) *Tart {
return &Tart{} return &Tart{stopTimeoutSeconds: stopTimeoutSeconds}
} }
func (tart *Tart) ID() v1.Runtime { func (tart *Tart) ID() v1.Runtime {
@@ -34,7 +36,8 @@ func (tart *Tart) NewVM(
softnetPolicyUpdates bool, softnetPolicyUpdates bool,
logger *zap.SugaredLogger, logger *zap.SugaredLogger,
) vmmanager.VM { ) vmmanager.VM {
return tartpkg.NewVM(vmResource, eventStreamer, vmPullTimeHistogram, dialer, softnetPolicyUpdates, logger) return tartpkg.NewVM(vmResource, eventStreamer, vmPullTimeHistogram, dialer,
softnetPolicyUpdates, logger, tart.stopTimeoutSeconds)
} }
func (tart *Tart) ListVMs(ctx context.Context, logger *zap.SugaredLogger) ([]vmmanager.VMInfo, error) { func (tart *Tart) ListVMs(ctx context.Context, logger *zap.SugaredLogger) ([]vmmanager.VMInfo, error) {
+6 -4
View File
@@ -12,10 +12,12 @@ import (
"go.uber.org/zap" "go.uber.org/zap"
) )
type Vetu struct{} type Vetu struct {
stopTimeoutSeconds uint16
}
func NewVetu() *Vetu { func NewVetu(stopTimeoutSeconds uint16) *Vetu {
return &Vetu{} return &Vetu{stopTimeoutSeconds: stopTimeoutSeconds}
} }
func (vetu *Vetu) ID() v1.Runtime { func (vetu *Vetu) ID() v1.Runtime {
@@ -34,7 +36,7 @@ func (vetu *Vetu) NewVM(
_ bool, _ bool,
logger *zap.SugaredLogger, logger *zap.SugaredLogger,
) vmmanager.VM { ) vmmanager.VM {
return vetupkg.NewVM(vmResource, eventStreamer, vmPullTimeHistogram, dialer, logger) return vetupkg.NewVM(vmResource, eventStreamer, vmPullTimeHistogram, dialer, logger, vetu.stopTimeoutSeconds)
} }
func (vetu *Vetu) ListVMs(ctx context.Context, logger *zap.SugaredLogger) ([]vmmanager.VMInfo, error) { func (vetu *Vetu) ListVMs(ctx context.Context, logger *zap.SugaredLogger) ([]vmmanager.VMInfo, error) {
+2 -2
View File
@@ -26,10 +26,10 @@ func TestStopWaitsForCommandAndRun(t *testing.T) {
new func(v1.VM) vmmanager.VM new func(v1.VM) vmmanager.VM
}{ }{
{name: "tart", new: func(resource v1.VM) vmmanager.VM { {name: "tart", new: func(resource v1.VM) vmmanager.VM {
return tart.NewVM(resource, nil, nil, nil, false, zap.NewNop().Sugar()) return tart.NewVM(resource, nil, nil, nil, false, zap.NewNop().Sugar(), 5)
}}, }},
{name: "vetu", new: func(resource v1.VM) vmmanager.VM { {name: "vetu", new: func(resource v1.VM) vmmanager.VM {
return vetu.NewVM(resource, nil, nil, nil, zap.NewNop().Sugar()) return vetu.NewVM(resource, nil, nil, nil, zap.NewNop().Sugar(), 5)
}}, }},
} }
+8 -4
View File
@@ -38,8 +38,9 @@ type VM struct {
wg *sync.WaitGroup wg *sync.WaitGroup
stopMtx sync.Mutex stopMtx sync.Mutex
stopDone chan error stopDone chan error
stopTimeoutSeconds uint16
dialer dialer.Dialer dialer dialer.Dialer
@@ -57,6 +58,7 @@ func NewVM(
dialer dialer.Dialer, dialer dialer.Dialer,
softnetPolicyUpdates bool, softnetPolicyUpdates bool,
logger *zap.SugaredLogger, logger *zap.SugaredLogger,
stopTimeoutSeconds uint16,
) *VM { ) *VM {
vmContext, vmContextCancel := context.WithCancel(context.Background()) vmContext, vmContextCancel := context.WithCancel(context.Background())
@@ -75,7 +77,8 @@ func NewVM(
ctx: vmContext, ctx: vmContext,
cancel: vmContextCancel, cancel: vmContextCancel,
wg: &sync.WaitGroup{}, wg: &sync.WaitGroup{},
stopTimeoutSeconds: stopTimeoutSeconds,
dialer: dialer, dialer: dialer,
softnetPolicyUpdates: softnetPolicyUpdates, softnetPolicyUpdates: softnetPolicyUpdates,
@@ -490,7 +493,8 @@ func (vm *VM) Stop() <-chan error {
go func() { go func() {
if ctx.Err() == nil { if ctx.Err() == nil {
// Try to gracefully terminate the VM. // Try to gracefully terminate the VM.
_, _, _ = Tart(context.WithoutCancel(ctx), zap.NewNop().Sugar(), "stop", "--timeout", "5", vm.id()) _, _, _ = Tart(context.WithoutCancel(ctx), zap.NewNop().Sugar(), "stop", "--timeout",
strconv.FormatUint(uint64(vm.stopTimeoutSeconds), 10), vm.id())
} }
// Cancellation requests shutdown; it does not establish completion. // Cancellation requests shutdown; it does not establish completion.
+8 -4
View File
@@ -33,8 +33,9 @@ type VM struct {
wg *sync.WaitGroup wg *sync.WaitGroup
stopMtx sync.Mutex stopMtx sync.Mutex
stopDone chan error stopDone chan error
stopTimeoutSeconds uint16
dialer dialer.Dialer dialer dialer.Dialer
@@ -47,6 +48,7 @@ func NewVM(
vmPullTimeHistogram metric.Float64Histogram, vmPullTimeHistogram metric.Float64Histogram,
dialer dialer.Dialer, dialer dialer.Dialer,
logger *zap.SugaredLogger, logger *zap.SugaredLogger,
stopTimeoutSeconds uint16,
) *VM { ) *VM {
vmContext, vmContextCancel := context.WithCancel(context.Background()) vmContext, vmContextCancel := context.WithCancel(context.Background())
@@ -62,7 +64,8 @@ func NewVM(
ctx: vmContext, ctx: vmContext,
cancel: vmContextCancel, cancel: vmContextCancel,
wg: &sync.WaitGroup{}, wg: &sync.WaitGroup{},
stopTimeoutSeconds: stopTimeoutSeconds,
dialer: dialer, dialer: dialer,
@@ -281,7 +284,8 @@ func (vm *VM) Stop() <-chan error {
go func() { go func() {
if ctx.Err() == nil { if ctx.Err() == nil {
// Try to gracefully terminate the VM. // Try to gracefully terminate the VM.
_, _, _ = Vetu(context.WithoutCancel(ctx), zap.NewNop().Sugar(), "stop", "--timeout", "5", vm.id()) _, _, _ = Vetu(context.WithoutCancel(ctx), zap.NewNop().Sugar(), "stop", "--timeout",
strconv.FormatUint(uint64(vm.stopTimeoutSeconds), 10), vm.id())
} }
// Cancellation requests shutdown; it does not establish completion. // Cancellation requests shutdown; it does not establish completion.
+16 -9
View File
@@ -7,6 +7,7 @@ import (
"net/http" "net/http"
"os" "os"
"slices" "slices"
"strconv"
"time" "time"
goruntime "runtime" goruntime "runtime"
@@ -48,6 +49,8 @@ const (
onDiskVMSyncTimeout = 30 * time.Second onDiskVMSyncTimeout = 30 * time.Second
tartVersionSoftnetPolicyUpdates = "2.34.0" tartVersionSoftnetPolicyUpdates = "2.34.0"
DefaultVMStopTimeoutSeconds = 5
) )
var ( var (
@@ -72,6 +75,7 @@ type Worker struct {
runtime runtime.Runtime runtime runtime.Runtime
softnetPolicyUpdates mo.Option[bool] softnetPolicyUpdates mo.Option[bool]
vmStopTimeoutSeconds uint16
vmPullTimeHistogram metric.Float64Histogram vmPullTimeHistogram metric.Float64Histogram
@@ -82,11 +86,12 @@ type Worker struct {
func New(client *client.Client, opts ...Option) (*Worker, error) { func New(client *client.Client, opts ...Option) (*Worker, error) {
worker := &Worker{ worker := &Worker{
client: client, client: client,
pollTicker: time.NewTicker(pollInterval), pollTicker: time.NewTicker(pollInterval),
recoveredVMs: make(map[ondiskname.OnDiskName]time.Time), recoveredVMs: make(map[ondiskname.OnDiskName]time.Time),
vmm: vmmanager.New(), vmm: vmmanager.New(),
syncRequested: make(chan bool, 1), syncRequested: make(chan bool, 1),
vmStopTimeoutSeconds: DefaultVMStopTimeoutSeconds,
} }
// Apply options // Apply options
@@ -110,9 +115,9 @@ func New(client *client.Client, opts ...Option) (*Worker, error) {
if worker.runtime == nil { if worker.runtime == nil {
if goruntime.GOOS == "linux" { if goruntime.GOOS == "linux" {
worker.runtime = runtime.NewVetu() worker.runtime = runtime.NewVetu(worker.vmStopTimeoutSeconds)
} else { } else {
worker.runtime = runtime.NewTart() worker.runtime = runtime.NewTart(worker.vmStopTimeoutSeconds)
} }
} }
@@ -920,7 +925,8 @@ func (worker *Worker) syncOnDiskVMsWithInventory(ctx context.Context, vmInfos []
// On-disk VM doesn't exist on the controller nor in the Worker's VM manager, // On-disk VM doesn't exist on the controller nor in the Worker's VM manager,
// stop it (if applicable) and delete it // stop it (if applicable) and delete it
if vmInfo.Running { if vmInfo.Running {
_, _, err := worker.runtime.Cmd(ctx, worker.logger, "stop", vmInfo.Name) _, _, err := worker.runtime.Cmd(ctx, worker.logger, "stop", "--timeout",
strconv.FormatUint(uint64(worker.vmStopTimeoutSeconds), 10), vmInfo.Name)
if err != nil { if err != nil {
worker.logger.Warnf("failed to stop") worker.logger.Warnf("failed to stop")
} }
@@ -935,7 +941,8 @@ func (worker *Worker) syncOnDiskVMsWithInventory(ctx context.Context, vmInfos []
// but we've lost track of it, so shut it down (if applicable) // but we've lost track of it, so shut it down (if applicable)
// and report the error (if not failed yet) // and report the error (if not failed yet)
if vmInfo.Running { if vmInfo.Running {
_, _, err := worker.runtime.Cmd(ctx, worker.logger, "stop", vmInfo.Name) _, _, err := worker.runtime.Cmd(ctx, worker.logger, "stop", "--timeout",
strconv.FormatUint(uint64(worker.vmStopTimeoutSeconds), 10), vmInfo.Name)
if err != nil { if err != nil {
worker.logger.Warnf("failed to stop") worker.logger.Warnf("failed to stop")
} }
+1 -1
View File
@@ -232,7 +232,7 @@ func newWorkerWithFakeTart(
vmm: vmmanager.New(), vmm: vmmanager.New(),
pollTicker: pollTicker, pollTicker: pollTicker,
syncRequested: make(chan bool, 1), syncRequested: make(chan bool, 1),
runtime: runtime.NewTart(), runtime: runtime.NewTart(DefaultVMStopTimeoutSeconds),
logger: zap.NewNop().Sugar(), logger: zap.NewNop().Sugar(),
} }
} }