This commit is contained in:
Rongsong Shen 2026-07-23 12:35:10 +08:00
commit ff1be48001
38 changed files with 2292 additions and 35 deletions

View file

@ -461,3 +461,41 @@ func signalNumber(signame string, action int) int {
}
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.
func FetchFile(jobid uint64, filepath string, offset uint64, maxBytes uint32) (*FetchFileResult, error) {
cFilepath := C.CString(filepath)
defer C.free(unsafe.Pointer(cFilepath))
msg := C.xce_fetchfile_t{
jobid: C.uint64_t(jobid),
filepath: cFilepath,
offset: C.uint64_t(offset),
max_bytes: C.uint32_t(maxBytes),
}
var reply *C.xce_fetchfile_reply_t
rc := C.xcecli_fetchfile(gCtx.ctx, &msg, &reply)
if rc < 0 {
return nil, fmt.Errorf("xcecli_fetchfile failed: %d", int(rc))
}
if reply == nil {
return nil, fmt.Errorf("xcecli_fetchfile returned nil reply")
}
defer C.free(unsafe.Pointer(reply))
if reply.error != 0 {
return nil, fmt.Errorf("fetchfile remote error: %d", int(reply.error))
}
result := &FetchFileResult{
FileSize: uint64(reply.file_size),
BytesRead: uint32(reply.bytes_read),
}
if reply.bytes_read > 0 && reply.content != nil {
result.Content = C.GoBytes(unsafe.Pointer(reply.content), C.int(reply.bytes_read))
}
return result, nil
}

View file

@ -14,3 +14,10 @@ type BackendConfig struct {
// serve as a single-host fallback when Peers is empty.
Peers []string
}
// FetchFileResult holds the data returned by FetchFile.
type FetchFileResult struct {
Content []byte
FileSize uint64
BytesRead uint32
}

View file

@ -4,6 +4,7 @@ import (
"apiserver/models"
"encoding/json"
"time"
"fmt"
)
// JobID bit layout constants — mirrors src/include/config.h.
@ -76,6 +77,33 @@ func IsArrayJob(id uint64) bool {
return extractIDTag(id) == tagJobArray
}
// FormatJobID formats a packed 64-bit job ID as a human-readable string,
// matching the C fmt_jobid() output in src/lib/utils.c.
// - Regular job: "{cluster}-{apiserver}-{timestamp}-{sequence}"
// - Array child: "{cluster}-{apiserver}-{timestamp}-{base}[{idx}]"
func FormatJobID(uniqueJobID int64) string {
id := uint64(uniqueJobID)
clusterID := extractBits(id, clusterIDShift, nClusterIDBits)
apiserverID := extractBits(id, apiserverIDShift, nApiserverIDBits)
timestamp := extractBits(id, timestampShift, nTimestampBits)
if extractIDTag(id) == tagJobArray {
arrayBase := extractBits(id, sequenceHighShift, nSequenceBits-nArrayIdxBits) << uint(sequenceHighShift)
arrayIdx := extractBits(id, 0, nArrayIdxBits)
return fmt.Sprintf("%d-%d-%d-%d[%d]",
clusterID, apiserverID, timestamp, arrayBase, arrayIdx)
}
sequence := extractBits(id, 0, nSequenceBits)
return fmt.Sprintf("%d-%d-%d-%d",
clusterID, apiserverID, timestamp, sequence)
}
// WorkerDefaultOutputPath returns the output file path the worker uses by default
// for the given job ID and task index, matching uv_proc.c make_job_output_path().
func WorkerDefaultOutputPath(uniqueJobID int64, taskIdx int) string {
return fmt.Sprintf("/tmp/job_%s_task%d.out", FormatJobID(uniqueJobID), taskIdx)
}
// rawStartInfo mirrors the C job_startinfo_t fields used by the Go model.
type rawStartInfo struct {
cmdline string

View file

@ -41,3 +41,8 @@ func EvalSelectForHost(_, _ string) error { return errNoCGO }
// Cleanup stub — real implementation in clib.go (requires CGO).
func Cleanup() {}
// FetchFile stub — real implementation in clib.go (requires CGO).
func FetchFile(_ uint64, _ string, _ uint64, _ uint32) (*FetchFileResult, error) {
return nil, errNoCGO
}