| package executor |
|
|
| import ( |
| "context" |
| "fmt" |
| "os" |
| "path/filepath" |
| "strings" |
| "time" |
|
|
| "RYP/backend/compiler/models" |
| "RYP/backend/compiler/sandbox" |
| ) |
|
|
| const ( |
| defaultPythonImage = "python-runner:latest" |
| defaultCppImage = "cpp-runner:latest" |
| defaultJavaImage = "java-runner:latest" |
| ) |
|
|
| |
| func Execute(req models.RunRequest) (*models.ExecutionResult, error) { |
| |
| tmpDir, err := os.MkdirTemp("", "ryp_sandbox_*") |
| if err != nil { |
| return nil, fmt.Errorf("failed to create temp directory: %w", err) |
| } |
| |
| defer os.RemoveAll(tmpDir) |
|
|
| containerDir := "/sandbox" |
| ctx := context.Background() |
|
|
| var result *sandbox.ExecutionResult |
| var execErr error |
|
|
| switch normalizeLanguage(req.Language) { |
| case "python": |
| |
| scriptPath := filepath.Join(tmpDir, "main.py") |
| if err := os.WriteFile(scriptPath, []byte(req.Code), 0644); err != nil { |
| return nil, fmt.Errorf("failed to write python file: %w", err) |
| } |
|
|
| result, execErr = sandbox.RunWithDocker( |
| ctx, |
| runnerImage("RYP_PYTHON_IMAGE", defaultPythonImage), |
| tmpDir, |
| containerDir, |
| 3*time.Second, |
| []string{"python", "main.py"}, |
| req.CustomInput, |
| ) |
|
|
| case "cpp": |
| |
| srcPath := filepath.Join(tmpDir, "main.cpp") |
| if err := os.WriteFile(srcPath, []byte(req.Code), 0644); err != nil { |
| return nil, fmt.Errorf("failed to write cpp file: %w", err) |
| } |
|
|
| |
| compResult, err := sandbox.RunWithDocker( |
| ctx, |
| runnerImage("RYP_CPP_IMAGE", defaultCppImage), |
| tmpDir, |
| containerDir, |
| 5*time.Second, |
| []string{"g++", "main.cpp", "-o", "main"}, |
| "", |
| ) |
| if err != nil { |
| return nil, fmt.Errorf("compile error: %w", err) |
| } |
| if compResult.ExitCode != 0 { |
| |
| return &models.ExecutionResult{ |
| Stdout: compResult.Stdout, |
| Stderr: compResult.Stderr, |
| ExitCode: compResult.ExitCode, |
| DurationMs: compResult.DurationMs, |
| }, nil |
| } |
|
|
| |
| result, execErr = sandbox.RunWithDocker( |
| ctx, |
| runnerImage("RYP_CPP_IMAGE", defaultCppImage), |
| tmpDir, |
| containerDir, |
| 3*time.Second, |
| []string{"./main"}, |
| req.CustomInput, |
| ) |
|
|
| case "java": |
| |
| srcPath := filepath.Join(tmpDir, "Main.java") |
| if err := os.WriteFile(srcPath, []byte(req.Code), 0644); err != nil { |
| return nil, fmt.Errorf("failed to write java file: %w", err) |
| } |
|
|
| |
| compResult, err := sandbox.RunWithDocker( |
| ctx, |
| runnerImage("RYP_JAVA_IMAGE", defaultJavaImage), |
| tmpDir, |
| containerDir, |
| 5*time.Second, |
| []string{"javac", "Main.java"}, |
| "", |
| ) |
| if err != nil { |
| return nil, fmt.Errorf("compile error: %w", err) |
| } |
| if compResult.ExitCode != 0 { |
| |
| return &models.ExecutionResult{ |
| Stdout: compResult.Stdout, |
| Stderr: compResult.Stderr, |
| ExitCode: compResult.ExitCode, |
| DurationMs: compResult.DurationMs, |
| }, nil |
| } |
|
|
| |
| result, execErr = sandbox.RunWithDocker( |
| ctx, |
| runnerImage("RYP_JAVA_IMAGE", defaultJavaImage), |
| tmpDir, |
| containerDir, |
| 3*time.Second, |
| []string{"java", "Main"}, |
| req.CustomInput, |
| ) |
|
|
| default: |
| return nil, fmt.Errorf("unsupported language: %s", req.Language) |
| } |
|
|
| if execErr != nil { |
| return nil, fmt.Errorf("execution failed: %w", execErr) |
| } |
|
|
| return &models.ExecutionResult{ |
| Stdout: result.Stdout, |
| Stderr: result.Stderr, |
| ExitCode: result.ExitCode, |
| TimeLimitExceeded: result.TimeLimitExceeded, |
| DurationMs: result.DurationMs, |
| }, nil |
| } |
|
|
| func normalizeLanguage(language string) string { |
| switch strings.ToLower(strings.TrimSpace(language)) { |
| case "py", "python3": |
| return "python" |
| case "c++", "cpp": |
| return "cpp" |
| default: |
| return strings.ToLower(strings.TrimSpace(language)) |
| } |
| } |
|
|
| func runnerImage(envName, fallback string) string { |
| value := strings.TrimSpace(os.Getenv(envName)) |
| if value == "" { |
| return fallback |
| } |
| return value |
| } |
|
|