fix(apiserver): send the requested signal, force kill and checkpoint options (bug-1940)
Cause: ControlJob (backend/clib.go) looked its content argument up as a signal name, but the job control handlers pass a JSON JobControlEvent, so the lookup always missed and every kill sent TERM (stop TSTP, resume CONT); sig.force and ckpt.kill were hard-coded to 0 and the checkpoint method was never set. Fix: new pure jobCtrlParams (backend/convert_pure.go) decodes the event (or a bare signal name/number), validates the signal against JobSignalMapTable and returns an error instead of falling back, and maps force_kill to xce_signal_t.force (kill) or xce_ckpt_options_t.kill (checkpoint) plus the checkpoint method. ControlJob fills the C structs from it. Tests: backend/jobctrl_params_test.go. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
8f164b8205
commit
4d2e80c4bb
3 changed files with 236 additions and 25 deletions
|
|
@ -680,8 +680,9 @@ func SubmitJob(job *models.JobInfo, arraySize int) error {
|
|||
}
|
||||
|
||||
// ControlJob sends a job control action to the backend via xcecli_jobctrl.
|
||||
// action must be one of the models.JOB_* constants; content is an optional
|
||||
// signal name (e.g. "TERM") for kill operations.
|
||||
// action must be one of the models.JOB_* constants; content is the
|
||||
// JSON-encoded models.JobControlEvent (signal, force_kill, method) or a bare
|
||||
// signal name/number -- see jobCtrlParams (convert_pure.go).
|
||||
func ControlJob(jobid uint64, action int, content string) error {
|
||||
var ctrl C.xce_jobctrl_t
|
||||
ctrl.jobid = C.uint64_t(jobid)
|
||||
|
|
@ -693,19 +694,28 @@ func ControlJob(jobid uint64, action int, content string) error {
|
|||
// For signal-based actions set signo; for checkpoint set ckpt options.
|
||||
// xce_jobctrl_t.u is a C union — we use a static C helper to set the
|
||||
// right branch safely.
|
||||
args, err := jobCtrlParams(action, content)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
switch action {
|
||||
case models.JOB_STOP, models.JOB_RESUME, models.JOB_KILL:
|
||||
var sig C.xce_signal_t
|
||||
sig.signo = C.int(signalNumber(content, action))
|
||||
sig.force = 0
|
||||
sig.signo = C.int(args.Signo)
|
||||
sig.force = C.int(args.Force)
|
||||
C.jobctrl_set_siginfo(&ctrl, &sig)
|
||||
case models.JOB_CHKPNT:
|
||||
var ckpt C.xce_ckpt_options_t
|
||||
ckpt.kill = 0
|
||||
ckpt.kill = C.int(args.CkptKill)
|
||||
if args.Method != "" {
|
||||
// NULL makes xce_jobctrl_req() send "default"
|
||||
ckpt.method = C.CString(args.Method)
|
||||
defer C.free(unsafe.Pointer(ckpt.method))
|
||||
}
|
||||
C.jobctrl_set_ckptoption(&ctrl, &ckpt)
|
||||
}
|
||||
|
||||
_, err := callBackend("xcecli_jobctrl", func() int {
|
||||
_, err = callBackend("xcecli_jobctrl", func() int {
|
||||
return int(C.xcecli_jobctrl(gCtx.ctx, &ctrl))
|
||||
})
|
||||
return err
|
||||
|
|
@ -744,25 +754,6 @@ func ControlHost(hostname string, action int) error {
|
|||
return err
|
||||
}
|
||||
|
||||
// signalNumber maps a job action and optional signal name to a POSIX signal
|
||||
// number, falling back to sensible defaults.
|
||||
func signalNumber(signame string, action int) int {
|
||||
if signame != "" {
|
||||
if n, ok := models.JobSignalMapTable[signame]; ok {
|
||||
return n
|
||||
}
|
||||
}
|
||||
switch action {
|
||||
case models.JOB_KILL:
|
||||
return models.JobSignalMapTable["TERM"] // 15
|
||||
case models.JOB_STOP:
|
||||
return models.JobSignalMapTable["TSTP"] // 20
|
||||
case models.JOB_RESUME:
|
||||
return models.JobSignalMapTable["CONT"] // 18
|
||||
}
|
||||
return 15 // SIGTERM as safe default
|
||||
}
|
||||
|
||||
// FetchFile fetches up to maxBytes of a job output file from offset via the
|
||||
// controller→worker chain. The filepath must be the fully resolved absolute
|
||||
// path on the exec host.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue