Reuse MockSlurm + MockMinIO + TestEnv wiring pattern to create a standalone binary that serves all API endpoints with in-memory SQLite and seed data. - internal/mockserver/server.go: assembly logic (New/Close/Run), option pattern, 4 accessors for seed data injection - cmd/mockserver/main.go: CLI flags (--port, --seed, MOCK_PORT), 6 seed jobs in all 5 states + 2 seed applications, signal handling Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
132 lines
3.2 KiB
Go
132 lines
3.2 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"flag"
|
|
"fmt"
|
|
"io"
|
|
"log"
|
|
"net/http"
|
|
"os"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"gcy_hpc_server/internal/mockserver"
|
|
"gcy_hpc_server/internal/model"
|
|
"gcy_hpc_server/internal/slurm"
|
|
)
|
|
|
|
func main() {
|
|
defaultPort := 8080
|
|
if v := os.Getenv("MOCK_PORT"); v != "" {
|
|
if p, err := strconv.Atoi(v); err == nil {
|
|
defaultPort = p
|
|
}
|
|
}
|
|
|
|
port := flag.Int("port", defaultPort, "listen port")
|
|
seed := flag.Bool("seed", true, "inject seed data")
|
|
flag.Parse()
|
|
|
|
srv, err := mockserver.New(mockserver.WithPort(*port))
|
|
if err != nil {
|
|
log.Fatalf("failed to create server: %v", err)
|
|
}
|
|
|
|
if *seed {
|
|
injectSeedData(srv)
|
|
}
|
|
|
|
if err := srv.Run(); err != nil {
|
|
log.Fatalf("server error: %v", err)
|
|
}
|
|
}
|
|
|
|
func injectSeedData(srv *mockserver.Server) {
|
|
client := srv.MockSlurmHTTPClient()
|
|
baseURL := srv.MockSlurmURL()
|
|
|
|
type jobDef struct {
|
|
name string
|
|
partition string
|
|
states []string
|
|
}
|
|
|
|
jobs := []jobDef{
|
|
{"gpu-training-job", "gpu", nil},
|
|
{"data-processing", "normal", []string{"RUNNING"}},
|
|
{"benchmark-test", "normal", []string{"RUNNING"}},
|
|
{"model-evaluation", "gpu", []string{"RUNNING", "COMPLETED"}},
|
|
{"failed-script", "normal", []string{"RUNNING", "FAILED"}},
|
|
{"cancelled-job", "gpu", []string{"RUNNING", "CANCELLED"}},
|
|
}
|
|
|
|
mockSlurm := srv.MockSlurm()
|
|
for _, j := range jobs {
|
|
id, err := submitJob(client, baseURL, j.name, j.partition)
|
|
if err != nil {
|
|
log.Printf("warning: failed to submit job %q: %v", j.name, err)
|
|
continue
|
|
}
|
|
for _, state := range j.states {
|
|
mockSlurm.SetJobState(id, state)
|
|
}
|
|
}
|
|
|
|
appSvc := srv.ApplicationService()
|
|
ctx := context.Background()
|
|
|
|
apps := []model.CreateApplicationRequest{
|
|
{
|
|
Name: "图像分类训练",
|
|
Description: "基于ResNet的图像分类模型训练",
|
|
ScriptTemplate: "#!/bin/bash\n#SBATCH --job-name={{.Name}}\necho 'Running {{.Name}}'",
|
|
},
|
|
{
|
|
Name: "数据处理流水线",
|
|
Description: "ETL数据处理脚本",
|
|
ScriptTemplate: "#!/bin/bash\necho 'Processing data'",
|
|
},
|
|
}
|
|
for _, app := range apps {
|
|
if _, err := appSvc.CreateApplication(ctx, &app); err != nil {
|
|
log.Printf("warning: failed to create application %q: %v", app.Name, err)
|
|
}
|
|
}
|
|
|
|
log.Println("Seeded 6 jobs (1 PENDING, 2 RUNNING, 1 COMPLETED, 1 FAILED, 1 CANCELLED) and 2 applications")
|
|
}
|
|
|
|
func submitJob(client *http.Client, baseURL, name, partition string) (int32, error) {
|
|
body := fmt.Sprintf(
|
|
`{"script":"#!/bin/bash\necho %s","job":{"name":"%s","partition":"%s"}}`,
|
|
name, name, partition,
|
|
)
|
|
req, err := http.NewRequest("POST", baseURL+"/slurm/v0.0.40/job/submit", strings.NewReader(body))
|
|
if err != nil {
|
|
return 0, fmt.Errorf("create request: %w", err)
|
|
}
|
|
req.Header.Set("Content-Type", "application/json")
|
|
|
|
resp, err := client.Do(req)
|
|
if err != nil {
|
|
return 0, fmt.Errorf("do request: %w", err)
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode != http.StatusOK {
|
|
b, _ := io.ReadAll(resp.Body)
|
|
return 0, fmt.Errorf("unexpected status %d: %s", resp.StatusCode, b)
|
|
}
|
|
|
|
var result slurm.OpenapiJobSubmitResponse
|
|
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
|
|
return 0, fmt.Errorf("decode response: %w", err)
|
|
}
|
|
if result.JobID == nil {
|
|
return 0, fmt.Errorf("no job_id in response")
|
|
}
|
|
return *result.JobID, nil
|
|
}
|