func NewContext(reader io.Reader) (*Context, error) { ctx, err := common.NewContextFromReader(reader) if err != nil { return nil, err } var context = &Context{ Context: *ctx, QueueManager: NewQueueManager(ctx.Config.Grader.ChannelLength), InflightMonitor: NewInflightMonitor(), InputManager: common.NewInputManager(ctx), } return context, nil }
func main() { flag.Parse() config := common.DefaultConfig() if *runtimePath != "" { config.Runner.PreserveFiles = true } else { var err error if *runtimePath, err = ioutil.TempDir("", "runner"); err != nil { panic(err) } defer os.RemoveAll(*runtimePath) } config.Logging.File = "stderr" if *verbose { config.Logging.Level = "debug" } config.Runner.RuntimePath = *runtimePath config.Tracing.Enabled = false ctx, err := common.NewContext(&config) if err != nil { panic(err) } inputManager := common.NewInputManager(ctx) results, err := runner.RunHostBenchmark( ctx, inputManager, &minijail, &ioLock, ) if err != nil { panic(err) } encoded, err := json.MarshalIndent(results, "", " ") if err != nil { panic(err) } os.Stdout.Write(encoded) }
func TestPreloadInputs(t *testing.T) { ctx, err := newRunnerContext() if err != nil { t.Fatalf("RunnerContext creation failed with %q", err) } defer ctx.Close() defer os.RemoveAll(ctx.Config.Runner.RuntimePath) inputManager := common.NewInputManager(ctx) inputPath := path.Join(ctx.Config.Runner.RuntimePath, "input") // Setting up files. dirs := []string{ "00/00000000000000000000000000000000000000", "00/00000000000000000000000000000000000001", "00/00000000000000000000000000000000000002", "00/00000000000000000000000000000000000003", "00/00000000000000000000000000000000000004", "00/00000000000000000000000000000000000005", "4b/ba61b5499a7a511eb515594f3293a8741516ad/in", "4b/ba61b5499a7a511eb515594f3293a8741516ad/out", } for _, d := range dirs { if err := os.MkdirAll(path.Join(inputPath, d), 0755); err != nil { t.Fatalf("Failed to create %q: %q", d, err) } } files := []struct { filename, contents string }{ { "00/00000000000000000000000000000000000000/settings.json", "{}", }, { "00/00000000000000000000000000000000000000.sha1", "0000000000000000000000000000000000000000 *00000000000000000000000000000000000000/settings.json", }, { "00/00000000000000000000000000000000000001/settings.json", "{}", }, { "00/00000000000000000000000000000000000002.sha1", "0000000000000000000000000000000000000000 *settings.json", }, { "00/00000000000000000000000000000000000003.sha1", "invalid sha1 file", }, { "00/00000000000000000000000000000000000004.sha1", "", }, { "00/00000000000000000000000000000000000005.sha1", "", }, { "00/00000000000000000000000000000000000005/settings.json", "invalid json", }, { "4b/ba61b5499a7a511eb515594f3293a8741516ad/in/0.in", "1 2", }, { "4b/ba61b5499a7a511eb515594f3293a8741516ad/out/0.out", "3", }, { "4b/ba61b5499a7a511eb515594f3293a8741516ad/settings.json", `{ "Cases": [ {"Cases": [{"Name": "0", "Weight": 1.0}], "Name": "0", "Weight": 1.0} ], "Limits": { "ExtraWallTime": 0, "MemoryLimit": 67108864, "OutputLimit": 16384, "OverallWallTimeLimit": 60000, "TimeLimit": 3000, "ValidatorTimeLimit": 3000 }, "Slow": false, "Validator": {"Name": "token-numeric"} }`, }, { "4b/ba61b5499a7a511eb515594f3293a8741516ad.sha1", `0849b07a45865a148f85f3ef389d6b6fa8e2d1fb *ba61b5499a7a511eb515594f3293a8741516ad/settings.json 3c28d037e32cd30eefd8183a83153083cced6cb7 *ba61b5499a7a511eb515594f3293a8741516ad/in/0.in 77de68daecd823babbb58edb1c8e14d7106e83bb *ba61b5499a7a511eb515594f3293a8741516ad/out/0.out`, }, } for _, ft := range files { if err := ioutil.WriteFile( path.Join(inputPath, ft.filename), []byte(ft.contents), 0644, ); err != nil { t.Fatalf("Failed to write file: %q", err) } } inputManager.PreloadInputs( inputPath, NewRunnerCachedInputFactory(inputPath), &sync.Mutex{}, ) hashentries := []struct { hash string valid bool }{ {"0000000000000000000000000000000000000000", false}, {"0000000000000000000000000000000000000001", false}, {"0000000000000000000000000000000000000002", false}, {"0000000000000000000000000000000000000003", false}, {"0000000000000000000000000000000000000004", false}, {"4bba61b5499a7a511eb515594f3293a8741516ad", true}, } for _, het := range hashentries { input, err := inputManager.Get(het.hash) if input != nil { defer input.Release(input) } if het.valid { if err != nil { t.Errorf("InputManager.Get(%q) == %q, want nil", het.hash, err) } } else { if err == nil { t.Errorf("InputManager.Get(%q) == %q, want !nil", het.hash, err) } } } }
func TestInputFactory(t *testing.T) { ctx, err := newRunnerContext() if err != nil { t.Fatalf("RunnerContext creation failed with %q", err) } defer ctx.Close() defer os.RemoveAll(ctx.Config.Runner.RuntimePath) type httpentry struct { content []byte mime string headers map[string]string } httpentries := map[string]httpentry{ "/input/4bba61b5499a7a511eb515594f3293a8741516ad/": httpentry{ mustDecode( `H4sIAFBMXVYAA+2WzU+DMBTAd93+CsJZWUspEK/Gmx+HLXowHppZZx0UA2VqFv53X1HYXDKXmDCi vt+B0r6Pvva1D5QeD7qGABHntqURJ5ttw9d3ShllA4d3HhlQFkbkjjNYinkutNmpt0/+S1F6TDyl O53DJjUMgm/yz7bzH0WQf9JpVJ/88/xTx+87BKRHstJ0/gH4Qf3nNMD6fwhs/okHzw7n2F//6Vb+ fYb1/zCwvgNAeqWQxig9L7ynIuvqL3Df/Q/hZ5/6oR/5Aec+1ALKOOF4/w/BauQ47qkoZOGeOLej 4XC17q3cS5FKeHOJe+S4N1LNHw10qUeqOxjYLQWnoGBdn6tUGevNTgT9s1eTixuRJFNVG5NaDQQX Ms3yt1odhsOIkjgOg0Z6VZrn0jRSGrJ4LVrKHPw1PlsP9mQ1OhMjZovWmgQxj8JWuGnFNoyuRaLu hcnybQUQVx+rmyTZCww+iKSQMDJc29gVtxtksoXUx7pMZa5mdrOmWQJB61m9Ax5ch2pUjfo+CgiC IAiCIAiCIAiCIAiCIAiC/BHeAU4V1PQAKAAA`, ), "application/x-gzip", map[string]string{ "Content-Sha1": "a32f0f018441df71efc8d1c6a55391354a8d4897", "X-Content-Uncompressed-Size": "20", }, }, "/input/0000000000000000000000000000000000000001/": httpentry{ []byte("invalid .tar.gz"), "application/x-gzip", map[string]string{ "Content-Sha1": "da39a3ee5e6b4b0d3255bfef95601890afd80709", "X-Content-Uncompressed-Size": "20", }, }, "/input/0000000000000000000000000000000000000002/": httpentry{ mustDecode( `H4sIAFBMXVYAA+2WzU+DMBTAd93+CsJZWUspEK/Gmx+HLXowHppZZx0UA2VqFv53X1HYXDKXmDCi vt+B0r6Pvva1D5QeD7qGABHntqURJ5ttw9d3ShllA4d3HhlQFkbkjjNYinkutNmpt0/+S1F6TDyl O53DJjUMgm/yz7bzH0WQf9JpVJ/88/xTx+87BKRHstJ0/gH4Qf3nNMD6fwhs/okHzw7n2F//6Vb+ fYb1/zCwvgNAeqWQxig9L7ynIuvqL3Df/Q/hZ5/6oR/5Aec+1ALKOOF4/w/BauQ47qkoZOGeOLej 4XC17q3cS5FKeHOJe+S4N1LNHw10qUeqOxjYLQWnoGBdn6tUGevNTgT9s1eTixuRJFNVG5NaDQQX Ms3yt1odhsOIkjgOg0Z6VZrn0jRSGrJ4LVrKHPw1PlsP9mQ1OhMjZovWmgQxj8JWuGnFNoyuRaLu hcnybQUQVx+rmyTZCww+iKSQMDJc29gVtxtksoXUx7pMZa5mdrOmWQJB61m9Ax5ch2pUjfo+CgiC IAiCIAiCIAiCIAiCIAiC/BHeAU4V1PQAKAAA`, ), "application/x-gzip", map[string]string{ "Content-Sha1": "Invalid SHA1 hash", "X-Content-Uncompressed-Size": "20", }, }, "/input/0000000000000000000000000000000000000003/": httpentry{ mustDecode( `H4sIAFBMXVYAA+2WzU+DMBTAd93+CsJZWUspEK/Gmx+HLXowHppZZx0UA2VqFv53X1HYXDKXmDCi vt+B0r6Pvva1D5QeD7qGABHntqURJ5ttw9d3ShllA4d3HhlQFkbkjjNYinkutNmpt0/+S1F6TDyl O53DJjUMgm/yz7bzH0WQf9JpVJ/88/xTx+87BKRHstJ0/gH4Qf3nNMD6fwhs/okHzw7n2F//6Vb+ fYb1/zCwvgNAeqWQxig9L7ynIuvqL3Df/Q/hZ5/6oR/5Aec+1ALKOOF4/w/BauQ47qkoZOGeOLej 4XC17q3cS5FKeHOJe+S4N1LNHw10qUeqOxjYLQWnoGBdn6tUGevNTgT9s1eTixuRJFNVG5NaDQQX Ms3yt1odhsOIkjgOg0Z6VZrn0jRSGrJ4LVrKHPw1PlsP9mQ1OhMjZovWmgQxj8JWuGnFNoyuRaLu hcnybQUQVx+rmyTZCww+iKSQMDJc29gVtxtksoXUx7pMZa5mdrOmWQJB61m9Ax5ch2pUjfo+CgiC IAiCIAiCIAiCIAiCIAiC/BHeAU4V1PQAKAAA`, ), "application/x-gzip", map[string]string{ "Content-Sha1": "a32f0f018441df71efc8d1c6a55391354a8d4897", "X-Content-Uncompressed-Size": "Invalid length", }, }, "/input/0000000000000000000000000000000000000004/": httpentry{ mustDecode("H4sIAJctXVYAA8vMK0vMyUxRKEksUjAYKMAFAMWXZ/uGAAAA"), "application/x-gzip", map[string]string{}, }, } ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { entry, ok := httpentries[r.RequestURI] if !ok { w.WriteHeader(http.StatusNotFound) return } for k, v := range entry.headers { w.Header().Add(k, v) } w.Write(entry.content) })) defer ts.Close() ctx.Config.Runner.GraderURL = ts.URL inputManager := common.NewInputManager(ctx) hashentries := []struct { hash string valid bool }{ {"0000000000000000000000000000000000000000", false}, {"0000000000000000000000000000000000000001", false}, {"0000000000000000000000000000000000000002", false}, {"0000000000000000000000000000000000000003", false}, {"0000000000000000000000000000000000000004", false}, {"4bba61b5499a7a511eb515594f3293a8741516ad", true}, } factory := NewRunnerInputFactory(http.DefaultClient, &ctx.Config) for _, het := range hashentries { input, err := inputManager.Add(het.hash, factory) if input != nil { defer input.Release(input) } if het.valid { if err != nil { t.Errorf("Input creation failed with %q", err) } } else { if err == nil { t.Errorf("Input creation succeeded, but was expected to fail") } } } }
func TestLibinteractive(t *testing.T) { minijail := &MinijailSandbox{} if testing.Short() { t.Skip("skipping test in short mode.") } if !minijail.Supported() { t.Skip("minijail sandbox not supported") } ctx, err := newRunnerContext() if err != nil { t.Fatalf("RunnerContext creation failed with %q", err) } defer ctx.Close() if !ctx.Config.Runner.PreserveFiles { defer os.RemoveAll(ctx.Config.Runner.RuntimePath) } inputManager := common.NewInputManager(ctx) AplusB, err := common.NewLiteralInputFactory( &common.LiteralInput{ Cases: map[string]common.LiteralCaseSettings{ "0": {Input: "1 2", ExpectedOutput: "3"}, "1": {Input: "2 3", ExpectedOutput: "5"}, }, Validator: &common.LiteralValidatorSettings{ Name: "token-numeric", }, Interactive: &common.LiteralInteractiveSettings{ IDLSource: ` interface Main {}; interface AplusB { int sum(int a, int b); }; interface Identity { int identity(int x); }; `, MainSource: ` #include "AplusB.h" #include <iostream> using namespace std; int main() { int A, B; cin >> A >> B; cout << identity(sum(identity(A), identity(B))) << endl; } `, ModuleName: "AplusB", ParentLang: "cpp", }, }, ctx.Config.Runner.RuntimePath, ) if err != nil { t.Fatalf("Failed to create Input: %q", err) } inputManager.Add(AplusB.Hash(), AplusB) input, err := inputManager.Get(AplusB.Hash()) if err != nil { t.Fatalf("Failed to open problem: %q", err) } runtests := []runnerTestCase{ { "cpp", ` #include <iostream> using namespace std; int main(int argc, char* argv[]) { int A, B; cin >> A >> B; cerr << argv[1] << endl; cout << A + B << endl; } `, "CE", 0.0, expectedResult{ "", ` File "test.py", line 1 if ^ SyntaxError: invalid syntax`, &RunMetadata{ExitStatus: 1, Verdict: "RTE"}, }, map[string]expectedResult{}, }, { "cpp11", ` #include "AplusB.h" int sum(int A, int B) { return -1; } int identity(int x) { return -1; } `, "WA", 0.0, expectedResult{"", "", &RunMetadata{Verdict: "OK"}}, map[string]expectedResult{ "0": {"-1", "", &RunMetadata{Verdict: "OK"}}, "1": {"-1", "", &RunMetadata{Verdict: "OK"}}, }, }, { "cpp", ` #include "AplusB.h" int sum(int A, int B) { return A + B; } int identity(int x) { return x; } `, "AC", 1.0, expectedResult{"", "", &RunMetadata{Verdict: "OK"}}, map[string]expectedResult{ "0": {"3", "", &RunMetadata{Verdict: "OK"}}, "1": {"5", "", &RunMetadata{Verdict: "OK"}}, }, }, { "java", ` class AplusB { public static int sum(int A, int B) { return A + B; } } class Identity { public static int identity(int x) { return x; } } `, "AC", 1.0, expectedResult{"", "", &RunMetadata{Verdict: "OK"}}, map[string]expectedResult{ "0": {"3", "", &RunMetadata{Verdict: "OK"}}, "1": {"5", "", &RunMetadata{Verdict: "OK"}}, }, }, { "py", "def sum(A, B):\n return A + B\ndef identity(x):\n return x", "AC", 1.0, expectedResult{"", "", &RunMetadata{Verdict: "OK"}}, map[string]expectedResult{ "0": {"3", "", &RunMetadata{Verdict: "OK"}}, "1": {"5", "", &RunMetadata{Verdict: "OK"}}, }, }, } for idx, rte := range runtests { results, err := Grade( ctx, &bytes.Buffer{}, &common.Run{ AttemptID: uint64(idx), Language: rte.language, InputHash: input.Hash(), Source: rte.source, MaxScore: 1.0, }, input, minijail, ) if err != nil { t.Errorf("Failed to run %v: %q", rte, err) continue } if results.Verdict != rte.expectedVerdict { t.Errorf( "results.Verdict = %q, expected %q", results.Verdict, rte.expectedVerdict, ) } if results.Score != rte.expectedScore { t.Errorf( "results.Score = %v, expected %v", results.Score, rte.expectedScore, ) } } }
func runGraderTests(t *testing.T, wrapper sandboxWrapper) { ctx, err := newRunnerContext() if err != nil { t.Fatalf("RunnerContext creation failed with %q", err) } defer ctx.Close() if !ctx.Config.Runner.PreserveFiles { defer os.RemoveAll(ctx.Config.Runner.RuntimePath) } inputManager := common.NewInputManager(ctx) AplusB, err := common.NewLiteralInputFactory( &common.LiteralInput{ Cases: map[string]common.LiteralCaseSettings{ "0": {Input: "1 2", ExpectedOutput: "3"}, "1": {Input: "2 3", ExpectedOutput: "5"}, }, Validator: &common.LiteralValidatorSettings{ Name: "token-numeric", }, }, ctx.Config.Runner.RuntimePath, ) if err != nil { t.Fatalf("Failed to create Input: %q", err) } inputManager.Add(AplusB.Hash(), AplusB) input, err := inputManager.Get(AplusB.Hash()) if err != nil { t.Fatalf("Failed to open problem: %q", err) } runtests := []runnerTestCase{ { "py", "print sum(map(int, raw_input().strip().split()))", "AC", 1.0, expectedResult{"", "", &RunMetadata{Verdict: "OK"}}, map[string]expectedResult{ "0": {"3", "", &RunMetadata{Verdict: "OK"}}, "1": {"5", "", &RunMetadata{Verdict: "OK"}}, }, }, { "py", "print 3", "PA", 0.5, expectedResult{"", "", &RunMetadata{Verdict: "OK"}}, map[string]expectedResult{ "0": {"3", "", &RunMetadata{Verdict: "OK"}}, "1": {"3", "", &RunMetadata{Verdict: "OK"}}, }, }, { "py", "print 2", "WA", 0.0, expectedResult{"", "", &RunMetadata{Verdict: "OK"}}, map[string]expectedResult{ "0": {"2", "", &RunMetadata{Verdict: "OK"}}, "1": {"2", "", &RunMetadata{Verdict: "OK"}}, }, }, { "py", "if", "CE", 0.0, expectedResult{ "", ` File "test.py", line 1 if ^ SyntaxError: invalid syntax`, &RunMetadata{ExitStatus: 1, Verdict: "RTE"}, }, map[string]expectedResult{}, }, { "c", "#include <stdio.h>\nint main() { printf(\"3\\n\"); }", "PA", 0.5, expectedResult{"", "", &RunMetadata{Verdict: "OK"}}, map[string]expectedResult{ "0": {"3", "", &RunMetadata{Verdict: "OK"}}, "1": {"3", "", &RunMetadata{Verdict: "OK"}}, }, }, { "cpp", "#include <iostream>\nint main() { std::cout << \"3\\n\"; }", "PA", 0.5, expectedResult{"", "", &RunMetadata{Verdict: "OK"}}, map[string]expectedResult{ "0": {"3", "", &RunMetadata{Verdict: "OK"}}, "1": {"3", "", &RunMetadata{Verdict: "OK"}}, }, }, { "rb", "puts 3", "PA", 0.5, expectedResult{"", "", &RunMetadata{Verdict: "OK"}}, map[string]expectedResult{ "0": {"3", "", &RunMetadata{Verdict: "OK"}}, "1": {"3", "", &RunMetadata{Verdict: "OK"}}, }, }, { "hs", "main = putStrLn \"3\"", "PA", 0.5, expectedResult{"", "", &RunMetadata{Verdict: "OK"}}, map[string]expectedResult{ "0": {"3", "", &RunMetadata{Verdict: "OK"}}, "1": {"3", "", &RunMetadata{Verdict: "OK"}}, }, }, { "pas", `program Main; begin writeln ('3'); end.`, "PA", 0.5, expectedResult{"", "", &RunMetadata{Verdict: "OK"}}, map[string]expectedResult{ "0": {"3", "", &RunMetadata{Verdict: "OK"}}, "1": {"3", "", &RunMetadata{Verdict: "OK"}}, }, }, { "java", `class Main { public static void main(String[] args) { System.out.println('3'); } }`, "PA", 0.5, expectedResult{"", "", &RunMetadata{Verdict: "OK"}}, map[string]expectedResult{ "0": {"3", "", &RunMetadata{Verdict: "OK"}}, "1": {"3", "", &RunMetadata{Verdict: "OK"}}, }, }, { "cat", "data:application/zip;base64,UEsDBAoAAAAAAOWiUUjRnmdVAgAAAAIAAAAFABwAMC" + "5vdXRVVAkAAy1HxVYtR8VWdXgLAAEE6AMAAAToAwAAMwpQSwMECgAAAAAA56JRSFc5PQ" + "MCAAAAAgAAAAUAHAAxLm91dFVUCQADMUfFVjFHxVZ1eAsAAQToAwAABOgDAAA1ClBLAQ" + "IeAwoAAAAAAOWiUUjRnmdVAgAAAAIAAAAFABgAAAAAAAEAAAC0gQAAAAAwLm91dFVUBQ" + "ADLUfFVnV4CwABBOgDAAAE6AMAAFBLAQIeAwoAAAAAAOeiUUhXOT0DAgAAAAIAAAAFAB" + "gAAAAAAAEAAAC0gUEAAAAxLm91dFVUBQADMUfFVnV4CwABBOgDAAAE6AMAAFBLBQYAAA" + "AAAgACAJYAAACCAAAAAAA=", "AC", 1.0, expectedResult{"", "", &RunMetadata{Verdict: "OK"}}, map[string]expectedResult{ "0": {"3", "", &RunMetadata{Verdict: "OK"}}, "1": {"5", "", &RunMetadata{Verdict: "OK"}}, }, }, } for idx, rte := range runtests { results, err := Grade( ctx, &bytes.Buffer{}, &common.Run{ AttemptID: uint64(idx), Language: rte.language, InputHash: input.Hash(), Source: rte.source, MaxScore: 1.0, }, input, wrapper.sandbox(&rte), ) if err != nil { t.Errorf("Failed to run %v: %q", rte, err) continue } if results.Verdict != rte.expectedVerdict { t.Errorf( "results.Verdict = %q, expected %q, test %v", results.Verdict, rte.expectedVerdict, idx, ) } if results.Score != rte.expectedScore { t.Errorf( "results.Score = %v, expected %v", results.Score, rte.expectedScore, ) } } }
func main() { rand.Seed(time.Now().UTC().UnixNano()) flag.Parse() if err := loadContext(); err != nil { panic(err) } ctx := globalContext.Load().(*common.Context) expvar.Publish("config", &globalContext.Load().(*common.Context).Config) inputManager = common.NewInputManager(ctx) inputPath := path.Join(ctx.Config.Runner.RuntimePath, "input") go inputManager.PreloadInputs( inputPath, runner.NewRunnerCachedInputFactory(inputPath), &ioLock, ) transport := &http.Transport{ Dial: (&net.Dialer{ Timeout: 30 * time.Second, KeepAlive: 30 * time.Second, }).Dial, TLSHandshakeTimeout: 10 * time.Second, ExpectContinueTimeout: 1 * time.Second, } if !*insecure { cert, err := ioutil.ReadFile(ctx.Config.TLS.CertFile) if err != nil { panic(err) } certPool := x509.NewCertPool() certPool.AppendCertsFromPEM(cert) keyPair, err := tls.LoadX509KeyPair( ctx.Config.TLS.CertFile, ctx.Config.TLS.KeyFile, ) transport.TLSClientConfig = &tls.Config{ Certificates: []tls.Certificate{keyPair}, RootCAs: certPool, ClientAuth: tls.RequireAndVerifyClientCert, } if err != nil { panic(err) } if err := http2.ConfigureTransport(transport); err != nil { panic(err) } } client := &http.Client{Transport: transport} baseURL, err := url.Parse(ctx.Config.Runner.GraderURL) if err != nil { panic(err) } setupMetrics(ctx) ctx.Log.Info("omegaUp runner ready to serve") go func() { for { results, err := runner.RunHostBenchmark( ctx, inputManager, &minijail, &ioLock, ) if err != nil { ctx.Log.Error("Failed to run benchmark", "err", err) } else { ctx.Log.Info("Benchmark successful", "results", results) } gaugesUpdate(results) time.Sleep(time.Duration(1) * time.Minute) } }() var sleepTime float32 = 1 for { if err := processRun(ctx, client, baseURL); err != nil { if err, ok := err.(net.Error); ok && err.Timeout() { // Timeouts are expected. Just retry. sleepTime = 1 continue } ctx.Log.Error("error grading run", "err", err) // Randomized exponential backoff. time.Sleep(time.Duration(rand.Float32()*sleepTime) * time.Second) if sleepTime < 64 { sleepTime *= 2 } } else { sleepTime = 1 } } }