func processDiffResults(ctx context.Context, sub model.KeyedSubmission, tr model.DiffTestResult, bucket, testFile string, test *datastore.Key) (ts model.TestStats, err error) { var want io.ReadCloser want, err = util.Load(util.CloudContext(ctx), bucket, testFile) if err != nil { return } defer want.Close() have := strings.NewReader(tr.Stdout) diffLines, ok, err := compare(want, have) if err != nil { return } tr.DiffLines = diffLines tr.Failed = !ok ts = model.TestStats{ Stdout: tr.Stdout, Stderr: tr.Stderr, Test: test, Failed: !ok, } _, err = tr.PutWithParent(ctx, sub.Key) return }
func robot(ctx context.Context, t model.KeyedTest, sub model.KeyedSubmission, ball io.Reader) (err error) { log.Debugf(ctx, "Executing robot tester") var testMap io.ReadCloser if testMap, err = util.Load(util.CloudContext(ctx), util.TemplateBucket, t.Params["tests"]); err != nil { return } defer testMap.Close() var testMapBytes, stdinBytes []byte tr := tar.NewReader(ball) if _, err = tr.Next(); err != nil { return err } if stdinBytes, err = ioutil.ReadAll(tr); err != nil { return err } if testMapBytes, err = ioutil.ReadAll(testMap); err != nil { return } var m Map if err = json.Unmarshal(testMapBytes, &m); err != nil { return } moves, err := testRobot(m, string(stdinBytes)) if err != nil { // TODO(victorbalan): Pass the error to the ws so the client knows what he's doing wrong return } var body []byte if body, err = json.Marshal(moves); err != nil { return } return ws.Write(sub.Key.Parent(), body) }
// PostSubmission creates a new submission. func PostSubmission(ctx context.Context, w http.ResponseWriter, r *http.Request) (status int, err error) { if r.Method != "POST" { return http.StatusMethodNotAllowed, nil } p, ok := passenger.FromContext(ctx) if !ok { return http.StatusUnauthorized, nil } mediaType, _, err := mime.ParseMediaType(r.Header.Get("Content-Type")) if err != nil { return http.StatusBadRequest, err } if !strings.HasPrefix(mediaType, "multipart/") { return http.StatusUnsupportedMediaType, nil } resultKey, err := datastore.DecodeKey(mux.Vars(r)["resultKey"]) if err != nil { return http.StatusNotFound, err } if !util.HasParent(p.User, resultKey) { return http.StatusBadRequest, errors.New("cannot submit answer for other users") } taskKey, err := datastore.DecodeKey(mux.Vars(r)["taskKey"]) if err != nil { return http.StatusNotFound, err } if err := r.ParseMultipartForm(16 << 20); err != nil { return http.StatusBadRequest, err } files, ok := r.MultipartForm.File["files"] if !ok { return http.StatusBadRequest, errors.New("missing files") } var task model.Task if err = datastore.Get(ctx, taskKey, &task); err != nil { return http.StatusNotFound, err } // Furthermore, the name of the GCS object is derived from the of the // encapsulating Submission. To avoid race conditions, allocate an ID. low, _, err := datastore.AllocateIDs(ctx, model.SubmissionKind, resultKey, 1) if err != nil { return http.StatusInternalServerError, err } submissionKey := datastore.NewKey(ctx, model.SubmissionKind, "", low, resultKey) storedCode := model.StoredObject{ Bucket: util.SubmissionBucket(), Name: nameObject(submissionKey) + "/Code/", } submission := model.Submission{ Task: taskKey, Time: time.Now(), Language: detectLanguage(files), Code: storedCode, } if _, err = datastore.Put(ctx, submissionKey, &submission); err != nil { return http.StatusInternalServerError, err } var tests model.Tests testKeys, err := model.NewQueryForTest(). Ancestor(taskKey). GetAll(ctx, &tests) if err != nil { return http.StatusInternalServerError, err } prrs, pwrs := multiPipe(len(tests)) go maketar(pwrs, files) for i, t := range tests { go func(i int, t model.Test) { if err := test.Tester(t.Tester).Call(ctx, *t.Key(testKeys[i]), *submission.Key(submissionKey), prrs[i]); err != nil { log.Warningf(ctx, "%s", err) } }(i, t) } if err := upload(util.CloudContext(ctx), storedCode.Bucket, storedCode.Name, files); err != nil { return http.StatusInternalServerError, err } return http.StatusOK, nil }
func IODiffRun(ctx context.Context, t model.KeyedTest, sub model.KeyedSubmission, ball io.Reader) (ts model.TestStats, err error) { image := newImage(sub.Language) if err = prepareImage(image); err != nil { return } var c *docker.Container c, err = dc.CreateContainer(docker.CreateContainerOptions{ Config: &docker.Config{ Image: image, OpenStdin: true, StdinOnce: true, }, HostConfig: &docker.HostConfig{ Privileged: false, Memory: 0, // TODO(flowlo): Limit memory }, }) if err != nil { return } err = dc.UploadToContainer(c.ID, docker.UploadToContainerOptions{ Path: "/run", InputStream: ball, }) if err != nil { return } var stdin io.ReadCloser stdin, err = util.Load(util.CloudContext(ctx), util.TestsBucket, t.Params["input"]) if err != nil { return } defer stdin.Close() start := time.Now() if err = dc.StartContainer(c.ID, c.HostConfig); err != nil { return } err = dc.AttachToContainer(docker.AttachToContainerOptions{ Container: c.ID, InputStream: stdin, Stdin: true, Stream: true, }) if err != nil { return } if err = waitForContainer(c.ID); err != nil { return } end := time.Now() stdout, stderr := new(bytes.Buffer), new(bytes.Buffer) if stdout, stderr, err = getLogs(c.ID); err != nil { return } tr := model.DiffTestResult{ SimpleTestResult: model.SimpleTestResult{ Stdout: stdout.String(), Stderr: stderr.String(), Start: start, End: end, }, Endpoint: "diff-result", } return processDiffResults(ctx, sub, tr, util.TestsBucket, t.Params["output"], t.Key) }
func readFromGCS(so model.StoredObject) (io.ReadCloser, error) { return util.Load(util.CloudContext(nil), so.Bucket, so.Name) }