tools: persist spark_submit calls to SubmissionRepo
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -10,11 +10,15 @@ import (
|
||||
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
|
||||
"spark-mcp-go/internal/audit"
|
||||
"spark-mcp-go/internal/cluster"
|
||||
"spark-mcp-go/internal/httpclient"
|
||||
"spark-mcp-go/internal/storage"
|
||||
"spark-mcp-go/internal/uploads"
|
||||
)
|
||||
|
||||
func TestSparkSubmit_StructuredCommand(t *testing.T) {
|
||||
deps, repo, _ := testDepsWithDataDir(t)
|
||||
deps, repo, _, _ := testDepsWithDataDir(t)
|
||||
deps.SparkSubmitTimeout = 5 * time.Second
|
||||
store := deps.UploadStore
|
||||
|
||||
@@ -25,7 +29,7 @@ func TestSparkSubmit_StructuredCommand(t *testing.T) {
|
||||
|
||||
echoed := filepath.Join(t.TempDir(), "echoed")
|
||||
echoScript := filepath.Join(t.TempDir(), "echo-args.sh")
|
||||
if err := os.WriteFile(echoScript, []byte("#!/bin/sh\nfor arg do\n echo \"$arg\" >> \"$ECHO_FILE\"\ndone\n"), 0o755); err != nil {
|
||||
if err := os.WriteFile(echoScript, []byte("#!/bin/sh\nfor arg do\n echo \"$arg\" >> \"$ECHO_FILE\"\ndone\necho 'Submitted application application_test_0001'\n"), 0o755); err != nil {
|
||||
t.Fatalf("write echo script: %v", err)
|
||||
}
|
||||
|
||||
@@ -97,8 +101,107 @@ func TestSparkSubmit_StructuredCommand(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestSparkSubmit_PersistsSubmission(t *testing.T) {
|
||||
// Open a dedicated DB so we can insert upload_files metadata for the join.
|
||||
db, err := storage.Open(":memory:")
|
||||
if err != nil {
|
||||
t.Fatalf("open db: %v", err)
|
||||
}
|
||||
t.Cleanup(func() { _ = db.Close() })
|
||||
|
||||
dataDir := t.TempDir()
|
||||
store, err := uploads.New(filepath.Join(dataDir, "uploads"))
|
||||
if err != nil {
|
||||
t.Fatalf("new upload store: %v", err)
|
||||
}
|
||||
|
||||
deps := &Deps{
|
||||
HTTPClient: httpclient.New(httpclient.Config{Timeout: 5 * time.Second, MaxResponseBytes: 1 << 20}),
|
||||
AuditRepo: audit.NewRepo(db),
|
||||
ClusterRepo: db.Clusters(),
|
||||
SubmissionRepo: db.Submissions(),
|
||||
MaxResponseBytes: 1 << 20,
|
||||
DataDir: dataDir,
|
||||
UploadStore: store,
|
||||
SparkSubmitTimeout: 5 * time.Second,
|
||||
}
|
||||
repo := db.Clusters()
|
||||
subRepo := db.Submissions()
|
||||
|
||||
fileID, _, _, _, absPath, err := store.Save([]byte("print('hi')\n"), "hello.py")
|
||||
if err != nil {
|
||||
t.Fatalf("save upload: %v", err)
|
||||
}
|
||||
if err := db.Uploads().Create(context.Background(), fileID, "hello.py", 14, "dummy-sha", time.Now()); err != nil {
|
||||
t.Fatalf("create upload record: %v", err)
|
||||
}
|
||||
|
||||
echoScript := filepath.Join(t.TempDir(), "echo-submit.sh")
|
||||
if err := os.WriteFile(echoScript, []byte("#!/bin/sh\necho 'tracking URL: http://rm.example.com:8088/proxy/application_persist_0001/' >&2\necho 'Submitted application application_persist_0001'\n"), 0o755); err != nil {
|
||||
t.Fatalf("write echo script: %v", err)
|
||||
}
|
||||
|
||||
createCluster(t, repo, &cluster.Cluster{
|
||||
ID: "cluster-persist",
|
||||
Name: "Persist",
|
||||
IsActive: true,
|
||||
AuthType: cluster.AuthNone,
|
||||
SparkSubmitExecuteBin: echoScript,
|
||||
})
|
||||
|
||||
req := newToolRequest(SparkSubmitName, map[string]any{
|
||||
"cluster_id": "cluster-persist",
|
||||
"master": "yarn",
|
||||
"deploy_mode": "cluster",
|
||||
"script_path": absPath,
|
||||
"queue": "default",
|
||||
"executor_memory": "2G",
|
||||
"executor_cores": 1,
|
||||
"num_executors": 4,
|
||||
})
|
||||
res, err := deps.SparkSubmitHandler(context.Background(), req)
|
||||
if err != nil {
|
||||
t.Fatalf("handler error: %v", err)
|
||||
}
|
||||
if res.IsError {
|
||||
t.Fatalf("unexpected error result: %v", res.Content)
|
||||
}
|
||||
|
||||
payload := resultJSON(t, res)
|
||||
if payload["app_id"] != "application_persist_0001" {
|
||||
t.Errorf("app_id=%v, want application_persist_0001", payload["app_id"])
|
||||
}
|
||||
if payload["tracking_url"] != "http://rm.example.com:8088/proxy/application_persist_0001/" {
|
||||
t.Errorf("tracking_url=%v, want tracking URL", payload["tracking_url"])
|
||||
}
|
||||
|
||||
subs, err := subRepo.List(context.Background(), storage.SubmissionFilter{Limit: 10})
|
||||
if err != nil {
|
||||
t.Fatalf("list submissions: %v", err)
|
||||
}
|
||||
if len(subs) != 1 {
|
||||
t.Fatalf("got %d submissions, want 1", len(subs))
|
||||
}
|
||||
s := subs[0]
|
||||
if s.FileID != fileID {
|
||||
t.Errorf("file_id=%q, want %q", s.FileID, fileID)
|
||||
}
|
||||
if s.AppID != "application_persist_0001" {
|
||||
t.Errorf("app_id=%q, want application_persist_0001", s.AppID)
|
||||
}
|
||||
if s.TrackingURL != "http://rm.example.com:8088/proxy/application_persist_0001/" {
|
||||
t.Errorf("tracking_url=%q, want tracking URL", s.TrackingURL)
|
||||
}
|
||||
if s.ClusterID != "cluster-persist" {
|
||||
t.Errorf("cluster_id=%q, want cluster-persist", s.ClusterID)
|
||||
}
|
||||
if s.FileName != "hello.py" {
|
||||
t.Errorf("file_name=%q, want hello.py", s.FileName)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSparkSubmit_MissingRequiredField(t *testing.T) {
|
||||
deps, _, _ := testDepsWithDataDir(t)
|
||||
deps, _, _, _ := testDepsWithDataDir(t)
|
||||
store := deps.UploadStore
|
||||
_, _, _, _, mintedPath, err := store.Save([]byte("# dummy\n"), "dummy.py")
|
||||
if err != nil {
|
||||
@@ -131,7 +234,7 @@ func TestSparkSubmit_MissingRequiredField(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestSparkSubmit_BadSparkConfValue(t *testing.T) {
|
||||
deps, _, _ := testDepsWithDataDir(t)
|
||||
deps, _, _, _ := testDepsWithDataDir(t)
|
||||
store := deps.UploadStore
|
||||
_, _, _, _, mintedPath, err := store.Save([]byte("# dummy\n"), "dummy.py")
|
||||
if err != nil {
|
||||
@@ -166,7 +269,7 @@ func TestSparkSubmit_BadSparkConfValue(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestSparkSubmit_RejectsNonMintedPath(t *testing.T) {
|
||||
deps, _, _ := testDepsWithDataDir(t)
|
||||
deps, _, _, _ := testDepsWithDataDir(t)
|
||||
|
||||
unminted := filepath.Join(t.TempDir(), "unminted.py")
|
||||
if err := os.WriteFile(unminted, []byte("print('not from upload_file')\n"), 0o644); err != nil {
|
||||
@@ -200,7 +303,7 @@ func TestSparkSubmit_RejectsNonMintedPath(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestSparkSubmit_EmptyMaster(t *testing.T) {
|
||||
deps, _, _ := testDepsWithDataDir(t)
|
||||
deps, _, _, _ := testDepsWithDataDir(t)
|
||||
store := deps.UploadStore
|
||||
_, _, _, _, mintedPath, err := store.Save([]byte("# dummy\n"), "dummy.py")
|
||||
if err != nil {
|
||||
|
||||
Reference in New Issue
Block a user