Add listing of runners file system.
This commit is contained in:
@ -100,6 +100,11 @@ type ExecutionResponse struct {
|
||||
WebSocketURL string `json:"websocketUrl"`
|
||||
}
|
||||
|
||||
// ListFileSystemResponse is the expected response when listing the file system.
|
||||
type ListFileSystemResponse struct {
|
||||
Files []FileHeader `json:"files"`
|
||||
}
|
||||
|
||||
// UpdateFileSystemRequest is the expected json structure of the request body for the update file system route.
|
||||
type UpdateFileSystemRequest struct {
|
||||
Delete []FilePath `json:"delete"`
|
||||
@ -109,6 +114,14 @@ type UpdateFileSystemRequest struct {
|
||||
// FilePath specifies the path of a file and is part of the UpdateFileSystemRequest.
|
||||
type FilePath string
|
||||
|
||||
// FileHeader specifies the information provided for listing a File.
|
||||
type FileHeader struct {
|
||||
Name FilePath `json:"name"`
|
||||
ObjectType string `json:"objectType"`
|
||||
Size int `json:"size"`
|
||||
ModificationTime int `json:"modificationTime"`
|
||||
}
|
||||
|
||||
// File is a DTO for transmitting file contents. It is part of the UpdateFileSystemRequest.
|
||||
type File struct {
|
||||
Path FilePath `json:"path"`
|
||||
|
126
pkg/nullio/ls2json.go
Normal file
126
pkg/nullio/ls2json.go
Normal file
@ -0,0 +1,126 @@
|
||||
package nullio
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/openHPI/poseidon/pkg/dto"
|
||||
"github.com/openHPI/poseidon/pkg/logging"
|
||||
"io"
|
||||
"regexp"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
var (
|
||||
log = logging.GetLogger("nullio")
|
||||
pathLineRegex = regexp.MustCompile(`(.*):$`)
|
||||
headerLineRegex = regexp.MustCompile(`([dl-])[-rwxXsS]{9} \d* .*? .*? +(\d+) (\d+) (.*)$`)
|
||||
)
|
||||
|
||||
// Ls2JsonWriter implements io.Writer.
|
||||
// It streams the passed data to the Target and transforms the data into the json format.
|
||||
type Ls2JsonWriter struct {
|
||||
Target io.Writer
|
||||
jsonStartSend bool
|
||||
setCommaPrefix bool
|
||||
remaining []byte
|
||||
latestPath []byte
|
||||
}
|
||||
|
||||
func (w *Ls2JsonWriter) Write(p []byte) (int, error) {
|
||||
i, err := w.initializeJSONObject()
|
||||
if err != nil {
|
||||
return i, err
|
||||
}
|
||||
|
||||
start := 0
|
||||
for i, char := range p {
|
||||
if char != '\n' {
|
||||
continue
|
||||
}
|
||||
|
||||
line := p[start:i]
|
||||
if len(w.remaining) > 0 {
|
||||
line = append(w.remaining, line...)
|
||||
w.remaining = []byte("")
|
||||
}
|
||||
|
||||
if len(line) != 0 {
|
||||
count, err := w.writeLine(line)
|
||||
if err != nil {
|
||||
log.WithError(err).Warn("Could not write line to Target")
|
||||
return count, err
|
||||
}
|
||||
}
|
||||
start = i + 1
|
||||
}
|
||||
|
||||
if start < len(p) {
|
||||
w.remaining = p[start:]
|
||||
}
|
||||
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
func (w *Ls2JsonWriter) initializeJSONObject() (count int, err error) {
|
||||
if !w.jsonStartSend {
|
||||
count, err = w.Target.Write([]byte("{\"files\": ["))
|
||||
if count == 0 || err != nil {
|
||||
log.WithError(err).Warn("Could not write to target")
|
||||
err = fmt.Errorf("could not write to target: %w", err)
|
||||
} else {
|
||||
w.jsonStartSend = true
|
||||
}
|
||||
}
|
||||
return count, err
|
||||
}
|
||||
|
||||
func (w *Ls2JsonWriter) Close() {
|
||||
count, err := w.Target.Write([]byte("]}"))
|
||||
if count == 0 || err != nil {
|
||||
log.WithError(err).Warn("Could not Close ls2json writer")
|
||||
}
|
||||
}
|
||||
|
||||
func (w *Ls2JsonWriter) writeLine(line []byte) (count int, err error) {
|
||||
matches := pathLineRegex.FindSubmatch(line)
|
||||
if matches != nil {
|
||||
w.latestPath = append(bytes.TrimSuffix(matches[1], []byte("/")), '/')
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
matches = headerLineRegex.FindSubmatch(line)
|
||||
if matches != nil {
|
||||
size, err1 := strconv.Atoi(string(matches[2]))
|
||||
timestamp, err2 := strconv.Atoi(string(matches[3]))
|
||||
if err1 != nil || err2 != nil {
|
||||
return 0, fmt.Errorf("could not parse file details: %w %+v", err1, err2)
|
||||
}
|
||||
|
||||
response, err1 := json.Marshal(dto.FileHeader{
|
||||
Name: dto.FilePath(append(w.latestPath, matches[4]...)),
|
||||
ObjectType: string(matches[1][0]),
|
||||
Size: size,
|
||||
ModificationTime: timestamp,
|
||||
})
|
||||
if err1 != nil {
|
||||
return 0, fmt.Errorf("could not marshal file header: %w", err)
|
||||
}
|
||||
|
||||
// Skip the first leading comma
|
||||
if w.setCommaPrefix {
|
||||
response = append([]byte{','}, response...)
|
||||
} else {
|
||||
w.setCommaPrefix = true
|
||||
}
|
||||
|
||||
count, err1 = w.Target.Write(response)
|
||||
if err1 != nil {
|
||||
err = fmt.Errorf("could not write to target: %w", err)
|
||||
} else if count == len(response) {
|
||||
count = len(line)
|
||||
}
|
||||
}
|
||||
|
||||
return count, err
|
||||
}
|
74
pkg/nullio/ls2json_test.go
Normal file
74
pkg/nullio/ls2json_test.go
Normal file
@ -0,0 +1,74 @@
|
||||
package nullio
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"github.com/stretchr/testify/suite"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestLs2JsonTestSuite(t *testing.T) {
|
||||
suite.Run(t, new(Ls2JsonTestSuite))
|
||||
}
|
||||
|
||||
type Ls2JsonTestSuite struct {
|
||||
suite.Suite
|
||||
buf *bytes.Buffer
|
||||
writer *Ls2JsonWriter
|
||||
}
|
||||
|
||||
func (s *Ls2JsonTestSuite) SetupTest() {
|
||||
s.buf = &bytes.Buffer{}
|
||||
s.writer = &Ls2JsonWriter{Target: s.buf}
|
||||
}
|
||||
|
||||
func (s *Ls2JsonTestSuite) TestLs2JsonWriter_WriteCreationAndClose() {
|
||||
count, err := s.writer.Write([]byte(""))
|
||||
s.Zero(count)
|
||||
s.NoError(err)
|
||||
|
||||
s.Equal("{\"files\": [", s.buf.String())
|
||||
|
||||
s.writer.Close()
|
||||
s.Equal("{\"files\": []}", s.buf.String())
|
||||
}
|
||||
|
||||
func (s *Ls2JsonTestSuite) TestLs2JsonWriter_WriteFile() {
|
||||
input := "total 0\n-rw-rw-r-- 1 kali kali 0 1660763446 flag\n"
|
||||
count, err := s.writer.Write([]byte(input))
|
||||
s.Equal(len(input), count)
|
||||
s.NoError(err)
|
||||
s.writer.Close()
|
||||
|
||||
s.Equal("{\"files\": [{\"name\":\"flag\",\"objectType\":\"-\",\"size\":0,\"modificationTime\":1660763446}]}",
|
||||
s.buf.String())
|
||||
}
|
||||
|
||||
func (s *Ls2JsonTestSuite) TestLs2JsonWriter_WriteRecursive() {
|
||||
input := ".:\ntotal 4\ndrwxrwxr-x 2 kali kali 4096 1660764411 dir\n" +
|
||||
"-rw-rw-r-- 1 kali kali 0 1660763446 flag\n" +
|
||||
"\n./dir:\ntotal 4\n-rw-rw-r-- 1 kali kali 3 1660764366 another.txt\n"
|
||||
count, err := s.writer.Write([]byte(input))
|
||||
s.Equal(len(input), count)
|
||||
s.NoError(err)
|
||||
s.writer.Close()
|
||||
|
||||
s.Equal("{\"files\": [{\"name\":\"./dir\",\"objectType\":\"d\",\"size\":4096,\"modificationTime\":1660764411},"+
|
||||
"{\"name\":\"./flag\",\"objectType\":\"-\",\"size\":0,\"modificationTime\":1660763446},"+
|
||||
"{\"name\":\"./dir/another.txt\",\"objectType\":\"-\",\"size\":3,\"modificationTime\":1660764366}]}",
|
||||
s.buf.String())
|
||||
}
|
||||
|
||||
func (s *Ls2JsonTestSuite) TestLs2JsonWriter_WriteRemaining() {
|
||||
input1 := "total 4\n-rw-rw-r-- 1 kali kali 3 1660764366 another.txt\n-rw-rw-r-- 1 kal"
|
||||
_, err := s.writer.Write([]byte(input1))
|
||||
s.NoError(err)
|
||||
s.Equal("{\"files\": [{\"name\":\"another.txt\",\"objectType\":\"-\",\"size\":3,\"modificationTime\":1660764366}",
|
||||
s.buf.String())
|
||||
|
||||
input2 := "i kali 0 1660763446 flag\n"
|
||||
_, err = s.writer.Write([]byte(input2))
|
||||
s.NoError(err)
|
||||
s.writer.Close()
|
||||
s.Equal("{\"files\": [{\"name\":\"another.txt\",\"objectType\":\"-\",\"size\":3,\"modificationTime\":1660764366},"+
|
||||
"{\"name\":\"flag\",\"objectType\":\"-\",\"size\":0,\"modificationTime\":1660763446}]}", s.buf.String())
|
||||
}
|
Reference in New Issue
Block a user