Add X11 forwarding for interactive jobs (xcecli -I -X, ssh -X style)
Render -X jobs' windows on the user's own X server with zero client install, mirroring the ssh -X experience: the job command starts with DISPLAY pointed at a per-job TCP listener on the worker, and the user's xcecli relays that listener's byte stream to their local X server. - xcecli: new -X/--x11 flag (interactive=2 + exdata "x11":1). After submit it fetches the per-job MIT-MAGIC-COOKIE-1, registers it in the user's Xauthority, then bridges one xattach websocket to the local X server address parsed from $DISPLAY (unix socket or TCP). The cookie entry is removed when the session ends. Pre-flight checks report missing DISPLAY/xauth before submit. - apiserver: GET /xce/v1/jobs/:id/xattach relays websocket binary frames to the worker's forwarding port (raw TCP, no ttyd). Close codes match the ttyd attach (4000 unreachable, 4001 ended). The job-finished event ends the relay. Teardown closes both hops as soon as either pump exits, so a client disconnect propagates instead of deadlocking in a blocking read. - worker: XJOB jobs with exdata x11=1 skip VNC, listen on 6100-6199, generate a per-job xauth file (/tmp/job_<id>_xauth, worker-side since the worker has no session), export DISPLAY/XAUTHORITY to the command, and bridge the app connection (loopback) with the relay connection (non-loopback; roles classified by source address, not arrival order). A 60 s no-client watchdog reaps jobs nothing ever attached to; the v1 single-connection boundary refuses a second X connection loudly. t1-verified per the plan matrix: window rendering (xclock/xeyes on Xvfb), normal end -> DONE + xauth removed, no-client 60 s reap (137), kill -> immediate teardown with no residue, -I/-I --browse regression, daemon health, missing-DISPLAY error, second-connection refusal. (--xjob VNC on t1 fails for lack of a vncserver binary; pre-existing.) Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
parent
4f188310ba
commit
85a22271c1
19 changed files with 1744 additions and 50 deletions
|
|
@ -580,6 +580,12 @@ type JobInfo struct {
|
|||
// JobAdditionalFields (the query-field whitelist) must stay in sync with
|
||||
// this name.
|
||||
InteractiveJobURL string `json:"interactive_job_url"`
|
||||
// X11Cookie is the per-job MIT-MAGIC-COOKIE-1 of an -X job (XJOB with
|
||||
// exdata x11=1), extracted from the same start-report exdata as
|
||||
// InteractiveJobURL. The xcecli -X client registers it with the user's
|
||||
// Xauthority before the app's first relayed X connection. Empty for
|
||||
// every other kind of job.
|
||||
X11Cookie string `json:"x11_cookie"`
|
||||
}
|
||||
|
||||
type JobExecHosts struct {
|
||||
|
|
@ -632,6 +638,7 @@ type JobAdditionalFields struct {
|
|||
Pids []int `json:"pids"` // List of process IDs associated with the job
|
||||
Pgids []int `json:"pgids"` // List of process group IDs associated with the job
|
||||
InteractiveJobURL string `json:"interactive_job_url"`
|
||||
X11Cookie string `json:"x11_cookie"` // -X job's per-session auth cookie (from start exdata)
|
||||
// CoreLimit/MemLimit: JobInfo has no equivalent field (these are
|
||||
// submission-time resource limits, not job-state fields), but xcecli's
|
||||
// JobFieldsMap accepts them as valid -o/-l display fields (bug-1674) --
|
||||
|
|
@ -642,38 +649,43 @@ type JobAdditionalFields struct {
|
|||
}
|
||||
|
||||
type JobSubmitRequest struct {
|
||||
JobName string `json:"job_name,omitempty" `
|
||||
Command string `json:"command" binding:"required"`
|
||||
QueueName string `json:"queue_name,omitempty"` // set to DEFAULT_QUEUE in config
|
||||
ProjectName string `json:"project_name,omitempty"` // default project: "default"
|
||||
UserGroup string `json:"user_group,omitempty"`
|
||||
LicenseProject string `json:"license_project,omitempty"`
|
||||
JobDescription string `json:"job_description,omitempty"`
|
||||
Resreq string `json:"resreq,omitempty"` // default: "type == local"
|
||||
Affinity string `json:"affinity,omitempty"`
|
||||
Gpureq string `json:"gpureq,omitempty"`
|
||||
RsvID string `json:"rsv_id,omitempty"`
|
||||
SLA string `json:"sla,omitempty"`
|
||||
MaxNumProcessors int `json:"maxnum_processors,omitempty"`
|
||||
NumProcessors int `json:"num_processors,omitempty"`
|
||||
AskedHosts string `json:"asked_hosts,omitempty"`
|
||||
LaunchType string `json:"launch_type,omitempty"`
|
||||
BeginTime CustomTime `json:"begin_time,omitempty"` //k change type
|
||||
TermTime CustomTime `json:"term_time,omitempty"` //k change type
|
||||
JobPriority int `json:"job_priority,omitempty"`
|
||||
DependCond string `json:"depend_cond,omitempty"`
|
||||
JobGroup string `json:"job_group,omitempty"`
|
||||
HostSpec string `json:"host_spec,omitempty"`
|
||||
Env []string `json:"env,omitempty"`
|
||||
Tags []string `json:"tags,omitempty"`
|
||||
Exclusive bool `json:"exclusive,omitempty"`
|
||||
Rerunable bool `json:"rerunable,omitempty"`
|
||||
Interactive int `json:"interactive,omitempty"`
|
||||
JobName string `json:"job_name,omitempty" `
|
||||
Command string `json:"command" binding:"required"`
|
||||
QueueName string `json:"queue_name,omitempty"` // set to DEFAULT_QUEUE in config
|
||||
ProjectName string `json:"project_name,omitempty"` // default project: "default"
|
||||
UserGroup string `json:"user_group,omitempty"`
|
||||
LicenseProject string `json:"license_project,omitempty"`
|
||||
JobDescription string `json:"job_description,omitempty"`
|
||||
Resreq string `json:"resreq,omitempty"` // default: "type == local"
|
||||
Affinity string `json:"affinity,omitempty"`
|
||||
Gpureq string `json:"gpureq,omitempty"`
|
||||
RsvID string `json:"rsv_id,omitempty"`
|
||||
SLA string `json:"sla,omitempty"`
|
||||
MaxNumProcessors int `json:"maxnum_processors,omitempty"`
|
||||
NumProcessors int `json:"num_processors,omitempty"`
|
||||
AskedHosts string `json:"asked_hosts,omitempty"`
|
||||
LaunchType string `json:"launch_type,omitempty"`
|
||||
BeginTime CustomTime `json:"begin_time,omitempty"` //k change type
|
||||
TermTime CustomTime `json:"term_time,omitempty"` //k change type
|
||||
JobPriority int `json:"job_priority,omitempty"`
|
||||
DependCond string `json:"depend_cond,omitempty"`
|
||||
JobGroup string `json:"job_group,omitempty"`
|
||||
HostSpec string `json:"host_spec,omitempty"`
|
||||
Env []string `json:"env,omitempty"`
|
||||
Tags []string `json:"tags,omitempty"`
|
||||
Exclusive bool `json:"exclusive,omitempty"`
|
||||
Rerunable bool `json:"rerunable,omitempty"`
|
||||
Interactive int `json:"interactive,omitempty"`
|
||||
// Attach marks an interactive TTY job as the terminal-attach kind
|
||||
// (xcecli -I without --browse): the session ends with the command, so
|
||||
// the worker may reap the ttyd when no client ever connects. 0 keeps
|
||||
// the web-viewed semantics.
|
||||
Attach int `json:"attach,omitempty"`
|
||||
Attach int `json:"attach,omitempty"`
|
||||
// X11 (exdata key "x11") selects the X11-forwarding backend for an XJOB
|
||||
// (interactive=2) job: the worker exports DISPLAY to its forwarding port
|
||||
// instead of starting VNC, and the app's X connection is relayed to the
|
||||
// user's local X server through the xattach websocket.
|
||||
X11 int `json:"x11,omitempty"`
|
||||
WarningTimePeriod int `json:"warning_time_period,omitempty"`
|
||||
WarningAction string `json:"warning_action,omitempty"`
|
||||
DataSpecFile string `json:"data_spec_file,omitempty"`
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue