forked from lei.chen/myxce
Complete the natural-language scheduler agent's Phase 0 read-only pipeline: Task #2 (API Key auth mechanism): - New store/apikey.go: APIKey store (hash, validate, expiry) - controllers/apikey.go: GenerateAPIKey finally persists + registers RBAC role - controllers/auth.go: GetUsernameFromToken recognizes xce_ak_ prefix - middleware/validatehandler.go: isValidToken handles API Key - config/rbac.yaml: mcp_readonly role (view-only permissions) - 6 new test files covering unit + integration full-chain Task #3 (schedulerclient Go library): - types.go: URM types (URMJob/URMQueue/URMNode/JobStatus/Metrics) - client.go: read-only Client interface (6 methods) - httpclient.go: resty-based HTTP implementation - statusmap.go + _test.go: TDD status mapping (33 tests) Task #4 (mcp-server binary): - cmd/main.go: entry, reads XCE_API_URL/XCE_API_KEY, registers 6 MCP tools - internal/app.go: tool handlers wiring schedulerclient to MCP stdio - Uses github.com/mark3labs/mcp-go@v0.57.0 for stdio transport All: go vet clean, go build clean, schedulerclient 33/33 tests pass. mcp-server binary ~11MB arm64, CGO_ENABLED=0. Architecture: Claude Code --stdio--> mcp-server --HTTP--> apiserver REST API --cgo--> C backend Co-Authored-By: Claude <noreply@anthropic.com>
93 lines
2.7 KiB
Go
93 lines
2.7 KiB
Go
package schedulerclient
|
|
|
|
import "strings"
|
|
|
|
// ── JobStatus mapping ───────────────────────────────────────────────────────
|
|
|
|
// JobStatusFromString maps the apiserver's job status string to a URM JobStatus.
|
|
//
|
|
// The input is the output of apiserver models.JobStateStr() — one of:
|
|
//
|
|
// PEND, PSUSP, RUN, USUSP, SSUSP, RUNKWN, EXIT, DONE, UNKWN, ERR, TRANSIENT
|
|
func JobStatusFromString(s string) JobStatus {
|
|
switch s {
|
|
case "PEND", "PSUSP":
|
|
return JobStatusPending
|
|
case "RUN", "RUNKWN":
|
|
return JobStatusRunning
|
|
case "USUSP", "SSUSP":
|
|
return JobStatusSuspended
|
|
case "EXIT":
|
|
return JobStatusFailed
|
|
case "DONE":
|
|
return JobStatusSucceeded
|
|
default:
|
|
// UNKWN, ERR, TRANSIENT, empty, or unknown values → unknown
|
|
return JobStatusUnknown
|
|
}
|
|
}
|
|
|
|
// ── QueueStatus mapping ─────────────────────────────────────────────────────
|
|
|
|
// QueueStatusFromString maps the apiserver's composite queue status string
|
|
// (e.g. "open:active", "closed:inactive_adm") to a URM status:
|
|
//
|
|
// active — queue is open and accepting jobs
|
|
// inactive — queue is administratively or window-closed
|
|
// draining — open but has an inactive-time window active (will close)
|
|
// unknown — unrecognised format
|
|
func QueueStatusFromString(s string) string {
|
|
if s == "" {
|
|
return "unknown"
|
|
}
|
|
|
|
// Format is "<open|closed>:<active|inactive_adm|inactive_win>"
|
|
parts := strings.SplitN(s, ":", 2)
|
|
if len(parts) < 1 {
|
|
return "unknown"
|
|
}
|
|
|
|
openClosed := parts[0]
|
|
var inactiveKind string
|
|
if len(parts) > 1 {
|
|
inactiveKind = parts[1]
|
|
}
|
|
|
|
switch openClosed {
|
|
case "open":
|
|
if inactiveKind == "inactive_adm" || inactiveKind == "inactive_win" {
|
|
return "draining"
|
|
}
|
|
return "active"
|
|
case "closed":
|
|
return "inactive"
|
|
default:
|
|
return "unknown"
|
|
}
|
|
}
|
|
|
|
// ── NodeStatus mapping ─────────────────────────────────────────────────────
|
|
|
|
// NodeStatusFromString maps the apiserver's host status string (output of
|
|
// models.HostStateStr()) to a URM node status:
|
|
//
|
|
// idle — OK, accepting work
|
|
// busy — running jobs
|
|
// down — CLOSED, CLOSED_BY_ADMIN
|
|
// drain — LOCKU, LOCKW (not accepting new work, existing jobs finish)
|
|
// unknown — UNAVAIL or unrecognised
|
|
func NodeStatusFromString(s string) string {
|
|
switch s {
|
|
case "OK":
|
|
return "idle"
|
|
case "BUSY":
|
|
return "busy"
|
|
case "CLOSED", "CLOSED_BY_ADMIN":
|
|
return "down"
|
|
case "LOCKU", "LOCKW":
|
|
return "drain"
|
|
default:
|
|
// UNAVAIL, empty, or unknown → unknown
|
|
return "unknown"
|
|
}
|
|
}
|