Skip to content

Commit

Permalink
downloading supporting files and submissions
Browse files Browse the repository at this point in the history
  • Loading branch information
robherley committed Apr 23, 2019
1 parent 1c058bb commit 7888112
Show file tree
Hide file tree
Showing 4 changed files with 103 additions and 38 deletions.
27 changes: 23 additions & 4 deletions grader/testdata.go → grader/grader.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package grader

import (
"errors"
"os/exec"
"strings"
"time"
)

Expand All @@ -25,19 +27,36 @@ type TestData struct {
func (data TestData) Grade() ([]WorkerResult, error) {
results := make(chan WorkerResult, len(data.Tests))

for _, test := range data.Tests {
go Worker(test, results)
for id, test := range data.Tests {
go Worker(id, test, results)
}

var testResults []WorkerResult
testResults := make([]WorkerResult, len(data.Tests))
for range data.Tests {
select {
case res := <-results:
testResults = append(testResults, res)
testResults[res.ID] = res
case <-time.After(2 * time.Minute):
return nil, errors.New("Timed out while running tests. (One Minute)")
}
}

return testResults, nil
}

// Build will build the specified
func (data TestData) Build() (map[string]string, error) {
cmds := strings.Split(data.TestBuildCMD, "\n")
totalOutput := make(map[string]string)
for _, cmd := range cmds {
args := strings.Fields(cmd)
buildCmd := exec.Command(args[0], args[1:]...)
buildOut, err := buildCmd.Output()
if err != nil {
return nil, err
}
totalOutput[cmd] = string(buildOut)
}

return totalOutput, nil
}
20 changes: 11 additions & 9 deletions grader/worker.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,29 +9,31 @@ import (

// WorkerResult is used in the channel to hold worker data
type WorkerResult struct {
Panicked bool `json:"panicked" binding:"required"`
Passed bool `json:"passed" binding:"required"`
Output string `json:"output" binding:"required"`
HTML string `json:"html" binding:"required"`
TestCMD string `json:"testCMD" binding:"required"`
Name string `json:"name" binding:"required"`
ID int `bson:"id" json:"id" binding:"required"`
Panicked bool `bson:"panicked" json:"panicked" binding:"required"`
Passed bool `bson:"passed" json:"passed" binding:"required"`
StudentFacing bool `bson:"studentFacing" json:"studentFacing" binding:"required"`
Output string `bson:"output" json:"output" binding:"required"`
HTML string `bson:"html" json:"html" binding:"required"`
TestCMD string `bson:"testCMD" json:"testCMD" binding:"required"`
Name string `bson:"name" json:"name" binding:"required"`
}

// Worker is used in a goroutine to exec a cmd in the job
func Worker(t Test, results chan WorkerResult) {
func Worker(id int, t Test, results chan WorkerResult) {
args := strings.Fields(t.TestCMD)
testCmd := exec.Command(args[0], args[1:]...)
testCmd.Dir = "/tmp/job"
testOut, err := testCmd.Output()
if err != nil {
results <- WorkerResult{Panicked: true, Passed: false, Output: "", HTML: "", TestCMD: t.TestCMD, Name: t.Name}
results <- WorkerResult{ID: id, Panicked: true, Passed: false, StudentFacing: t.StudentFacing, Output: "", HTML: "", TestCMD: t.TestCMD, Name: t.Name}
} else {
dmp := diffmatchpatch.New()
diffs := dmp.DiffMain(t.ExpectedOutput, string(testOut), false)
passed := true
if len(diffs) > 1 {
passed = false
}
results <- WorkerResult{Panicked: false, Passed: passed, Output: string(testOut), HTML: dmp.DiffPrettyHtml(diffs), TestCMD: t.TestCMD, Name: t.Name}
results <- WorkerResult{ID: id, Panicked: false, Passed: passed, StudentFacing: t.StudentFacing, Output: string(testOut), HTML: dmp.DiffPrettyHtml(diffs), TestCMD: t.TestCMD, Name: t.Name}
}
}
61 changes: 38 additions & 23 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"encoding/json"
"fmt"
"k8s-grader/grader"
"k8s-grader/utils"
"os"
"os/exec"

Expand All @@ -20,19 +21,51 @@ func main() {
testDataStr := os.Getenv("TEST_DATA")
secret := os.Getenv("JOB_SECRET")

fmt.Printf("API: %s\nData: %s\nSecret: %s\n", api, testDataStr, secret)
fmt.Println("\n[Brian]: ENV Variables:")
fmt.Printf("API: %s\nSecret: %s\nData: %s\n", api, secret, testDataStr)

var testData grader.TestData
if err := json.Unmarshal([]byte(testDataStr), &testData); err != nil {
panic(err)
}

// Change working directory
// Make Folder
mkdirCmd := exec.Command("mkdir", "-p", "/tmp/job")
if _, err := mkdirCmd.Output(); err != nil {
panic(err)
}

// Change CWD
if err := os.Chdir("/tmp/job"); err != nil {
panic(err)
}

// Download and extract supporting files
path := "/tmp/job/sup.tar.gz"
url := fmt.Sprintf("%s/job/%s/assignment/%s/supportingfiles/download", api, secret, testData.AssignmentID)
if err := utils.DownloadAndExtract(url, path, "tarball"); err != nil {
panic(err)
}

// Download and extract submission
path = "/tmp/job/sub.tar.gz"
url = fmt.Sprintf("%s/job/%s/submission/%s/download", api, secret, testData.SubmissionID)
if err := utils.DownloadAndExtract(url, path, "tarball"); err != nil {
panic(err)
}

// Build
fmt.Println("\n[Brian]: Starting Build Script")
buildOut, err := testData.Build()
if err != nil {
panic(err)
}

for k, v := range buildOut {
fmt.Printf("[Brian]: Build - Exec '%s' Output:\n%s", k, v)
}

// Grade the assignment
results, err := testData.Grade()
if err != nil {
panic(err)
Expand All @@ -43,28 +76,10 @@ func main() {
enc.SetEscapeHTML(false)
enc.SetIndent(" ", " ")
_ = enc.Encode(results)
fmt.Printf("\nTest Results: \n%+v", string(buf.String()))

return
// Download assignment
// job/:secret/submission/:sid/download
// path := "/tmp/submission.tar.gz"
// url := fmt.Sprintf("%s/job/%s/submission/%s/download", api, secret, subID)
// if err := utils.DownloadFile(path, url); err != nil {
// panic(err)
// }

// Download supporting files
// job/:secret/assignment/:aid/supportingfiles/download
// path = "/tmp/support.tar.gz"
// url = fmt.Sprintf("%s/job/%s/assignment/%s/supportingfiles/download", api, secret, assID)
// if err := utils.DownloadFile(path, url); err != nil {
// panic(err)
// }

// Grade Submission
fmt.Printf("\n[Brian]: Test Results: \n%+v", string(buf.String()))

// Send grade back to court-herald

// fmt.Printf("finished!")
fmt.Printf("\n[Brian]: Finished Successfully.\n")
return
}
33 changes: 31 additions & 2 deletions utils/utils.go
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
package utils

import (
"errors"
"io"
"net/http"
"os"
"os/exec"
)

// DownloadFile downloads a file to a specific filepath based on url
func DownloadFile(filepath string, url string) error {

func DownloadFile(url string, filepath string) error {
// Get the data
resp, err := http.Get(url)
if err != nil {
Expand All @@ -26,3 +27,31 @@ func DownloadFile(filepath string, url string) error {
_, err = io.Copy(out, resp.Body)
return err
}

// ExtractFile extracts a certain filetype based on the filepath and filetype
func ExtractFile(filepath string, filetype string) error {
switch filetype {
case "tarball":
untar := exec.Command("tar", "-xvf", filepath)

if _, err := untar.Output(); err != nil {
return err
}
return nil
case "zip":
return errors.New("zip not implemented")
default:
return errors.New("invalid format specified")
}
}

// DownloadAndExtract calls download and extract :)
func DownloadAndExtract(url string, filepath string, filetype string) error {
if err := DownloadFile(url, filepath); err != nil {
return err
}
if err := ExtractFile(filepath, filetype); err != nil {
return err
}
return nil
}

0 comments on commit 7888112

Please sign in to comment.