fix(apiserver): reject submit options the job never gets; add exec_home (bug-1936)
Cause: CreateJob accepted in_file, out_file, err_file, out_dir, asked_hosts, begin_time, term_time and transfer_files and returned success, but neither the controller's submit data/start info nor the worker carries any of them (the worker never stages transferred files), so they were silently dropped. Exec home had no submit field at all, and a relative cwd would have been resolved against the worker unit's directory. Fix: new preCheckSubmitOptions() runs right after binding, before file uploads and ID allocation, and answers 400 naming every unsupported option that is set; cwd and the new exec_home field must be absolute and fit the worker's job_assignment_t buffers (1023 bytes). exec_home goes to JobInfo.ExecHome and from there (submitStartInfo) to the start info's exechome. num_files already reaches exdata as nofile_limit; a handler test now pins it. The array %J/%I submit-time substitution test (9.4) checks cwd instead of out_file/err_file, which are now refused. Swagger regenerated with `swag init -g cmd/main.go -o docs`. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
725b1d23fd
commit
edad8c7385
6 changed files with 200 additions and 12 deletions
|
|
@ -1085,7 +1085,8 @@ func (j JobController) RestartJobByID(ctx *gin.Context) {
|
|||
|
||||
// CreateJob godoc
|
||||
// @Summary Create a new job
|
||||
// @Description Submit a new job with specified requirements and parameters
|
||||
// @Description Submit a new job with specified requirements and parameters.
|
||||
// @Description in_file, out_file, err_file, out_dir, asked_hosts, begin_time, term_time and transfer_files are not supported yet and are rejected with 400; cwd and exec_home must be absolute paths.
|
||||
// @Tags jobs
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
|
|
@ -1114,6 +1115,14 @@ func (j JobController) CreateJob(ctx *gin.Context) {
|
|||
return
|
||||
}
|
||||
|
||||
// bug-1936: refuse an option the job would never get, before anything
|
||||
// (file uploads, ID allocation) happens for it.
|
||||
if err := preCheckSubmitOptions(&req); err != nil {
|
||||
logger.Error("Job submission rejected", zap.Error(err))
|
||||
ReturnError(ctx, common.INVALID_PARAMS_CODE, err.Error(), nil)
|
||||
return
|
||||
}
|
||||
|
||||
// precheck the request parameter before generating the job ID
|
||||
arraySize, isArray, err := preCheckJobRequest(&req)
|
||||
if err != nil {
|
||||
|
|
@ -1220,6 +1229,7 @@ func (j JobController) CreateJob(ctx *gin.Context) {
|
|||
UserPriority: req.JobPriority,
|
||||
DependCond: req.DependCond,
|
||||
CWD: req.CWD,
|
||||
ExecHome: req.ExecHome, // bug-1936: exechome of the start info
|
||||
FromHost: req.FromHost, // bug-1676: client-asserted hostname, passed through unchanged
|
||||
PreExecCmd: req.PreExecCmd,
|
||||
PostExecCmd: req.PostExecCmd,
|
||||
|
|
@ -1281,6 +1291,62 @@ func (j JobController) CreateJob(ctx *gin.Context) {
|
|||
ReturnSuccess(ctx, common.CREATED, common.SUCCESS_MSG, response)
|
||||
}
|
||||
|
||||
// maxWorkerPathLen is the longest cwd or exec home the worker takes: xcecd
|
||||
// hands both to xcecd_worker in job_assignment_t's cwd/home[PATH_LENGTH]
|
||||
// (1024 bytes, src/worker/include/uv_ipc.h), NUL included.
|
||||
const maxWorkerPathLen = 1024 - 1
|
||||
|
||||
// preCheckSubmitOptions rejects what a submission asks for but the job
|
||||
// would not get (bug-1936). The controller's submit data and start info
|
||||
// have no field for stdin/stdout/stderr paths, an output directory, asked
|
||||
// hosts or a begin/termination time, and the worker never stages
|
||||
// transferred files in or out, so each of those is refused instead of
|
||||
// being accepted and dropped. cwd and exec_home do reach the worker, which
|
||||
// runs the job there with HOME set; both must be absolute and fit its
|
||||
// buffers.
|
||||
func preCheckSubmitOptions(req *models.JobSubmitRequest) error {
|
||||
unsupported := []struct {
|
||||
field string
|
||||
set bool
|
||||
}{
|
||||
{"in_file", req.InFile != ""},
|
||||
{"out_file", req.OutFile != ""},
|
||||
{"err_file", req.ErrFile != ""},
|
||||
{"out_dir", req.OutDir != ""},
|
||||
{"asked_hosts", strings.TrimSpace(req.AskedHosts) != ""},
|
||||
{"begin_time", !req.BeginTime.Time.IsZero()},
|
||||
{"term_time", !req.TermTime.Time.IsZero()},
|
||||
{"transfer_files", len(req.TransferFiles) > 0},
|
||||
}
|
||||
var names []string
|
||||
for _, u := range unsupported {
|
||||
if u.set {
|
||||
names = append(names, u.field)
|
||||
}
|
||||
}
|
||||
if len(names) > 0 {
|
||||
return fmt.Errorf("job submission option(s) %s not supported: the job would not get them",
|
||||
strings.Join(names, ", "))
|
||||
}
|
||||
|
||||
for _, p := range []struct{ field, value string }{
|
||||
{"cwd", req.CWD},
|
||||
{"exec_home", req.ExecHome},
|
||||
} {
|
||||
if p.value == "" {
|
||||
continue
|
||||
}
|
||||
if !strings.HasPrefix(p.value, "/") {
|
||||
return fmt.Errorf("%s %q must be an absolute path", p.field, p.value)
|
||||
}
|
||||
if len(p.value) > maxWorkerPathLen {
|
||||
return fmt.Errorf("%s is too long: %d bytes, more than the limit of %d",
|
||||
p.field, len(p.value), maxWorkerPathLen)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func preCheckJobRestartRequest(req *models.JobRestartRequest) error {
|
||||
logger.Debug("Enter preCheckJobRestartRequest")
|
||||
if err := validateAskedHosts(req.AskedHosts); err != nil {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue