Files
hpc/cmd/mockserver/main.go
dailz 9a04874847 feat(cmd/mockserver): add standalone mock server for frontend development
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>
2026-04-17 13:21:13 +08:00

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
}