Steal tests from other branch that is being discarded, attempt to get at least one of them to pass; WIP

This commit is contained in:
Dane Everitt 2020-09-30 21:46:32 -07:00
parent 0f7bb1a371
commit 9b7c0fb7f3
No known key found for this signature in database
GPG Key ID: EEA66103B3D71F53
6 changed files with 613 additions and 33 deletions

1
go.mod
View File

@ -24,6 +24,7 @@ require (
github.com/docker/go-metrics v0.0.1 // indirect
github.com/docker/go-units v0.4.0 // indirect
github.com/fatih/color v1.9.0
github.com/franela/goblin v0.0.0-20200825194134-80c0062ed6cd
github.com/frankban/quicktest v1.10.2 // indirect
github.com/fsnotify/fsnotify v1.4.9 // indirect
github.com/gabriel-vasile/mimetype v1.1.1

2
go.sum
View File

@ -123,6 +123,8 @@ github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5Kwzbycv
github.com/fatih/color v1.9.0 h1:8xPHl4/q1VyqGIPif1F+1V3Y3lSmrq01EabUW3CoW5s=
github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4=
github.com/franela/goblin v0.0.0-20200825194134-80c0062ed6cd h1:b/30UOB56Rhfe185ZfgvZT0/HOql0OzxuiNOxRKXRXc=
github.com/franela/goblin v0.0.0-20200825194134-80c0062ed6cd/go.mod h1:VzmDKDJVZI3aJmnRI9VjAn9nJ8qPPsN1fqzr9dqInIo=
github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20=
github.com/frankban/quicktest v1.10.2 h1:19ARM85nVi4xH7xPXuc5eM/udya5ieh7b/Sv+d844Tk=
github.com/frankban/quicktest v1.10.2/go.mod h1:K+q6oSqb0W0Ininfk863uOk1lMy69l/P6txr3mVT54s=

View File

@ -1,7 +1,6 @@
package router
import (
"bufio"
"context"
"github.com/gin-gonic/gin"
"github.com/pkg/errors"
@ -30,34 +29,12 @@ func getServerFileContents(c *gin.Context) {
}
p = "/" + strings.TrimLeft(p, "/")
cleaned, err := s.Filesystem().SafePath(p)
st, err := s.Filesystem().Stat(p)
if err != nil {
c.AbortWithStatusJSON(http.StatusNotFound, gin.H{
"error": "The file requested could not be found.",
})
TrackedServerError(err, s).AbortFilesystemError(c)
return
}
st, err := s.Filesystem().Stat(cleaned)
if err != nil {
TrackedServerError(err, s).AbortWithServerError(c)
return
}
if st.Info.IsDir() {
c.AbortWithStatusJSON(http.StatusNotFound, gin.H{
"error": "The requested resource was not found on the system.",
})
return
}
f, err := os.Open(cleaned)
if err != nil {
TrackedServerError(err, s).AbortWithServerError(c)
return
}
defer f.Close()
c.Header("X-Mime-Type", st.Mimetype)
c.Header("Content-Length", strconv.Itoa(int(st.Info.Size())))
@ -68,7 +45,10 @@ func getServerFileContents(c *gin.Context) {
c.Header("Content-Type", "application/octet-stream")
}
bufio.NewReader(f).WriteTo(c.Writer)
if err := s.Filesystem().Readfile(p, c.Writer); err != nil {
TrackedServerError(err, s).AbortFilesystemError(c)
return
}
}
// Returns the contents of a directory for a server.

View File

@ -199,7 +199,18 @@ func (fs *Filesystem) hasSpaceFor(size int64) error {
// Updates the disk usage for the Filesystem instance.
func (fs *Filesystem) addDisk(i int64) int64 {
size, _ := fs.DiskUsage(true)
var size = atomic.LoadInt64(&fs.diskUsed)
// Sorry go gods. This is ugly but the best approach I can come up with for right
// now without completely re-evaluating the logic we use for determining disk space.
//
// Normally I would just be using the atomic load right below, but I'm not sure about
// the scenarios where it is 0 because nothing has run that would trigger a disk size
// calculation?
//
// Perhaps that isn't even a concern for the sake of this?
if !fs.isTest {
size, _ = fs.DiskUsage(true)
}
// If we're dropping below 0 somehow just cap it to 0.
if (size + i) < 0 {

View File

@ -2,7 +2,6 @@ package filesystem
import (
"bufio"
"bytes"
"fmt"
"github.com/gabriel-vasile/mimetype"
"github.com/karrick/godirwalk"
@ -33,6 +32,8 @@ type Filesystem struct {
// The root data directory path for this Filesystem instance.
root string
isTest bool
}
// Creates a new Filesystem instance for a given server.
@ -53,18 +54,21 @@ func (fs *Filesystem) Path() string {
// Reads a file on the system and returns it as a byte representation in a file
// reader. This is not the most memory efficient usage since it will be reading the
// entirety of the file into memory.
func (fs *Filesystem) Readfile(p string) (io.Reader, error) {
func (fs *Filesystem) Readfile(p string, w io.Writer) error {
cleaned, err := fs.SafePath(p)
if err != nil {
return nil, err
return err
}
b, err := ioutil.ReadFile(cleaned)
f, err := os.Open(cleaned)
if err != nil {
return nil, err
return err
}
defer f.Close()
return bytes.NewReader(b), nil
_, err = bufio.NewReader(f).WriteTo(w)
return err
}
// Writes a file to the system. If the file does not already exist one will be created.
@ -179,6 +183,10 @@ func (fs *Filesystem) Chown(path string) error {
return errors.WithStack(err)
}
if fs.isTest {
return nil
}
uid := config.Get().System.User.Uid
gid := config.Get().System.User.Gid

View File

@ -0,0 +1,578 @@
package filesystem
import (
"bytes"
"errors"
. "github.com/franela/goblin"
"github.com/pterodactyl/wings/config"
"io/ioutil"
"math/rand"
"os"
"path/filepath"
"strings"
"testing"
"unicode/utf8"
)
func NewFs() (*Filesystem, *rootFs) {
config.Set(&config.Configuration{
AuthenticationToken: "abc",
System: config.SystemConfiguration{
RootDirectory: "/server",
DiskCheckInterval: 150,
},
})
tmpDir, err := ioutil.TempDir(os.TempDir(), "pterodactyl")
if err != nil {
panic(err)
}
// defer os.RemoveAll(tmpDir)
rfs := rootFs{root: tmpDir}
rfs.reset()
fs := New(filepath.Join(tmpDir, "/server"), 0)
fs.isTest = true
return fs, &rfs
}
type rootFs struct {
root string
}
func (rfs *rootFs) CreateServerFile(p string, c string) error {
f, err := os.Create(filepath.Join(rfs.root, "/server", p))
if err == nil {
f.Write([]byte(c))
f.Close()
}
return err
}
func (rfs *rootFs) StatServerFile(p string) (os.FileInfo, error) {
return os.Stat(filepath.Join(rfs.root, "/server", p))
}
func (rfs *rootFs) reset() {
if err := os.RemoveAll(filepath.Join(rfs.root, "/server")); err != nil {
if !os.IsNotExist(err) {
panic(err)
}
}
if err := os.Mkdir(filepath.Join(rfs.root, "/server"), 0755); err != nil {
panic(err)
}
}
func TestFilesystem_Path(t *testing.T) {
g := Goblin(t)
fs, rfs := NewFs()
g.Describe("Path", func() {
g.It("returns the root path for the instance", func() {
g.Assert(fs.Path()).Equal(filepath.Join(rfs.root, "/server"))
})
})
}
func TestFilesystem_Readfile(t *testing.T) {
g := Goblin(t)
fs, rfs := NewFs()
g.Describe("Readfile", func() {
buf := &bytes.Buffer{}
g.It("opens a file if it exists on the system", func() {
err := rfs.CreateServerFile("test.txt", "testing")
g.Assert(err).IsNil()
err = fs.Readfile("test.txt", buf)
g.Assert(err).IsNil()
g.Assert(buf.String()).Equal("testing")
})
g.It("returns an error if the file does not exist", func() {
err := fs.Readfile("test.txt", buf)
g.Assert(err).IsNotNil()
g.Assert(errors.Is(err, os.ErrNotExist)).IsTrue()
})
g.It("returns an error if the \"file\" is a directory", func() {
err := rfs.CreateServerFile("test.txt", "testing")
g.Assert(err).IsNil()
err = fs.Readfile("test.txt", buf)
g.Assert(err).IsNotNil()
g.Assert(errors.Is(err, ErrIsDirectory)).IsTrue()
})
g.It("cannot open a file outside the root directory", func() {
err := rfs.CreateServerFile("/../test.txt", "testing")
g.Assert(err).IsNil()
err = fs.Readfile("/../test.txt", buf)
g.Assert(err).IsNotNil()
g.Assert(strings.Contains(err.Error(), "file does not exist")).IsTrue()
})
g.AfterEach(func() {
buf.Truncate(0)
fs.diskUsed = 0
rfs.reset()
})
})
}
func TestFilesystem_Writefile(t *testing.T) {
g := Goblin(t)
fs, rfs := NewFs()
g.Describe("Open and WriteFile", func() {
buf := &bytes.Buffer{}
// Test that a file can be written to the disk and that the disk space used as a result
// is updated correctly in the end.
g.It("can create a new file", func() {
r := bytes.NewReader([]byte("test file content"))
g.Assert(fs.diskUsed).Equal(int64(0))
err := fs.Writefile("test.txt", r)
g.Assert(err).IsNil()
err = fs.Readfile("test.txt", buf)
g.Assert(err).IsNil()
g.Assert(buf.String()).Equal("test file content")
g.Assert(fs.diskUsed).Equal(r.Size())
})
g.It("can create a new file inside a nested directory with leading slash", func() {
r := bytes.NewReader([]byte("test file content"))
err := fs.Writefile("/some/nested/test.txt", r)
g.Assert(err).IsNil()
err = fs.Readfile("/some/nested/test.txt", buf)
g.Assert(err).IsNil()
g.Assert(buf.String()).Equal("test file content")
})
g.It("can create a new file inside a nested directory without a trailing slash", func() {
r := bytes.NewReader([]byte("test file content"))
err := fs.Writefile("some/../foo/bar/test.txt", r)
g.Assert(err).IsNil()
err = fs.Readfile("foo/bar/test.txt", buf)
g.Assert(err).IsNil()
g.Assert(buf.String()).Equal("test file content")
})
g.It("cannot create a file outside the root directory", func() {
r := bytes.NewReader([]byte("test file content"))
err := fs.Writefile("/some/../foo/../../test.txt", r)
g.Assert(err).IsNotNil()
g.Assert(strings.Contains(err.Error(), "file does not exist")).IsTrue()
})
g.It("cannot write a file that exceedes the disk limits", func() {
fs.diskLimit = 1024
b := make([]byte, 1025)
_, err := rand.Read(b)
g.Assert(err).IsNil()
g.Assert(len(b)).Equal(1025)
r := bytes.NewReader(b)
err = fs.Writefile("test.txt", r)
g.Assert(err).IsNotNil()
g.Assert(errors.Is(err, ErrNotEnoughDiskSpace)).IsTrue()
})
g.It("updates the total space used when a file is appended to", func() {
fs.diskUsed = 100
b := make([]byte, 100)
_, _ = rand.Read(b)
r := bytes.NewReader(b)
err := fs.Writefile("test.txt", r)
g.Assert(err).IsNil()
g.Assert(fs.diskUsed).Equal(int64(200))
// If we write less data than already exists, we should expect the total
// disk used to be decremented.
b = make([]byte, 50)
_, _ = rand.Read(b)
r = bytes.NewReader(b)
err = fs.Writefile("test.txt", r)
g.Assert(err).IsNil()
g.Assert(fs.diskUsed).Equal(int64(150))
})
g.It("truncates the file when writing new contents", func() {
r := bytes.NewReader([]byte("original data"))
err := fs.Writefile("test.txt", r)
g.Assert(err).IsNil()
r = bytes.NewReader([]byte("new data"))
err = fs.Writefile("test.txt", r)
g.Assert(err).IsNil()
err = fs.Readfile("test.txt", buf)
g.Assert(err).IsNil()
g.Assert(buf.String()).Equal("new data")
})
g.AfterEach(func() {
buf.Truncate(0)
rfs.reset()
fs.diskUsed = 0
fs.diskLimit = 0
})
})
}
func TestFilesystem_CreateDirectory(t *testing.T) {
g := Goblin(t)
fs, rfs := NewFs()
g.Describe("CreateDirectory", func() {
g.It("should create missing directories automatically", func() {
err := fs.CreateDirectory("test", "foo/bar/baz")
g.Assert(err).IsNil()
st, err := rfs.StatServerFile("foo/bar/baz/test")
g.Assert(err).IsNil()
g.Assert(st.IsDir()).IsTrue()
g.Assert(st.Name()).Equal("test")
})
g.It("should work with leading and trailing slashes", func() {
err := fs.CreateDirectory("test", "/foozie/barzie/bazzy/")
g.Assert(err).IsNil()
st, err := rfs.StatServerFile("foozie/barzie/bazzy/test")
g.Assert(err).IsNil()
g.Assert(st.IsDir()).IsTrue()
g.Assert(st.Name()).Equal("test")
})
g.It("should not allow the creation of directories outside the root", func() {
err := fs.CreateDirectory("test", "e/../../something")
g.Assert(err).IsNotNil()
g.Assert(strings.Contains(err.Error(), "file does not exist")).IsTrue()
})
g.It("should not increment the disk usage", func() {
err := fs.CreateDirectory("test", "/")
g.Assert(err).IsNil()
g.Assert(fs.diskUsed).Equal(int64(0))
})
g.AfterEach(func() {
rfs.reset()
})
})
}
func TestFilesystem_Rename(t *testing.T) {
g := Goblin(t)
fs, rfs := NewFs()
g.Describe("Rename", func() {
g.BeforeEach(func() {
if err := rfs.CreateServerFile("source.txt", "text content"); err != nil {
panic(err)
}
})
g.It("returns an error if the target already exists", func() {
err := rfs.CreateServerFile("target.txt", "taget content")
g.Assert(err).IsNil()
err = fs.Rename("source.txt", "target.txt")
g.Assert(err).IsNotNil()
g.Assert(errors.Is(err, os.ErrExist)).IsTrue()
})
g.It("returns an error if the final destination is the root directory", func() {
err := fs.Rename("source.txt", "/")
g.Assert(err).IsNotNil()
g.Assert(errors.Is(err, os.ErrExist)).IsTrue()
})
g.It("returns an error if the source destination is the root directory", func() {
err := fs.Rename("source.txt", "/")
g.Assert(err).IsNotNil()
g.Assert(errors.Is(err, os.ErrExist)).IsTrue()
})
g.It("does not allow renaming to a location outside the root", func() {
err := fs.Rename("source.txt", "../target.txt")
g.Assert(err).IsNotNil()
g.Assert(errors.Is(err, os.ErrNotExist)).IsTrue()
})
g.It("does not allow renaming from a location outside the root", func() {
err := rfs.CreateServerFile("/../ext-source.txt", "taget content")
err = fs.Rename("/../ext-source.txt", "target.txt")
g.Assert(err).IsNotNil()
g.Assert(errors.Is(err, os.ErrNotExist)).IsTrue()
})
g.It("allows a file to be renamed", func() {
err := fs.Rename("source.txt", "target.txt")
g.Assert(err).IsNil()
_, err = rfs.StatServerFile("source.txt")
g.Assert(err).IsNotNil()
g.Assert(errors.Is(err, os.ErrNotExist)).IsTrue()
st, err := rfs.StatServerFile("target.txt")
g.Assert(err).IsNil()
g.Assert(st.Name()).Equal("target.txt")
g.Assert(st.Size()).IsNotZero()
})
g.It("allows a folder to be renamed", func() {
err := os.Mkdir(filepath.Join(rfs.root, "/server/source_dir"), 0755)
g.Assert(err).IsNil()
err = fs.Rename("source_dir", "target_dir")
g.Assert(err).IsNil()
_, err = rfs.StatServerFile("source_dir")
g.Assert(err).IsNotNil()
g.Assert(errors.Is(err, os.ErrNotExist)).IsTrue()
st, err := rfs.StatServerFile("target_dir")
g.Assert(err).IsNil()
g.Assert(st.IsDir()).IsTrue()
})
g.It("returns an error if the source does not exist", func() {
err := fs.Rename("missing.txt", "target.txt")
g.Assert(err).IsNotNil()
g.Assert(errors.Is(err, os.ErrNotExist)).IsTrue()
})
g.It("creates directories if they are missing", func() {
err := fs.Rename("source.txt", "nested/folder/target.txt")
g.Assert(err).IsNil()
st, err := rfs.StatServerFile("nested/folder/target.txt")
g.Assert(err).IsNil()
g.Assert(st.Name()).Equal("target.txt")
})
g.AfterEach(func() {
rfs.reset()
})
})
}
func TestFilesystem_Copy(t *testing.T) {
g := Goblin(t)
fs, rfs := NewFs()
g.Describe("Copy", func() {
g.BeforeEach(func() {
if err := rfs.CreateServerFile("source.txt", "text content"); err != nil {
panic(err)
}
fs.diskUsed = int64(utf8.RuneCountInString("test content"))
})
g.It("should return an error if the source does not exist", func() {
err := fs.Copy("foo.txt")
g.Assert(err).IsNotNil()
g.Assert(errors.Is(err, os.ErrNotExist)).IsTrue()
})
g.It("should return an error if the source is outside the root", func() {
err := rfs.CreateServerFile("/../ext-source.txt", "text content")
err = fs.Copy("../ext-source.txt")
g.Assert(err).IsNotNil()
g.Assert(errors.Is(err, os.ErrNotExist)).IsTrue()
})
g.It("should return an error if the source directory is outside the root", func() {
err := os.MkdirAll(filepath.Join(rfs.root, "/nested/in/dir"), 0755)
g.Assert(err).IsNil()
err = rfs.CreateServerFile("/../nested/in/dir/ext-source.txt", "external content")
g.Assert(err).IsNil()
err = fs.Copy("../nested/in/dir/ext-source.txt")
g.Assert(err).IsNotNil()
g.Assert(errors.Is(err, os.ErrNotExist)).IsTrue()
err = fs.Copy("nested/in/../../../nested/in/dir/ext-source.txt")
g.Assert(err).IsNotNil()
g.Assert(errors.Is(err, os.ErrNotExist)).IsTrue()
})
g.It("should return an error if the source is a directory", func() {
err := os.Mkdir(filepath.Join(rfs.root, "/server/dir"), 0755)
g.Assert(err).IsNil()
err = fs.Copy("dir")
g.Assert(err).IsNotNil()
g.Assert(errors.Is(err, os.ErrNotExist)).IsTrue()
})
g.It("should return an error if there is not space to copy the file", func() {
fs.diskLimit = 2
err := fs.Copy("source.txt")
g.Assert(err).IsNotNil()
g.Assert(errors.Is(err, ErrNotEnoughDiskSpace)).IsTrue()
})
g.It("should create a copy of the file and increment the disk used", func() {
err := fs.Copy("source.txt")
g.Assert(err).IsNil()
_, err = rfs.StatServerFile("source.txt")
g.Assert(err).IsNil()
_, err = rfs.StatServerFile("source copy.txt")
g.Assert(err).IsNil()
})
g.It("should create a copy of the file with a suffix if a copy already exists", func() {
err := fs.Copy("source.txt")
g.Assert(err).IsNil()
err = fs.Copy("source.txt")
g.Assert(err).IsNil()
r := []string{"source.txt", "source copy.txt", "source copy 1.txt"}
for _, name := range r {
_, err = rfs.StatServerFile(name)
g.Assert(err).IsNil()
}
g.Assert(fs.diskUsed).Equal(int64(utf8.RuneCountInString("test content")) * 3)
})
g.It("should create a copy inside of a directory", func() {
err := os.MkdirAll(filepath.Join(rfs.root, "/server/nested/in/dir"), 0755)
g.Assert(err).IsNil()
err = rfs.CreateServerFile("source.txt", "test content")
g.Assert(err).IsNil()
err = fs.Copy("nested/in/dir/source.txt")
g.Assert(err).IsNil()
_, err = rfs.StatServerFile("nested/in/dir/source.txt")
g.Assert(err).IsNil()
_, err = rfs.StatServerFile("nested/in/dir/source copy.txt")
g.Assert(err).IsNil()
})
g.AfterEach(func() {
rfs.reset()
fs.diskUsed = 0
fs.diskLimit = 0
})
})
}
func TestFilesystem_Delete(t *testing.T) {
g := Goblin(t)
fs, rfs := NewFs()
g.Describe("Delete", func() {
g.BeforeEach(func() {
if err := rfs.CreateServerFile("source.txt", "test content"); err != nil {
panic(err)
}
fs.diskUsed = int64(utf8.RuneCountInString("test content"))
})
g.It("does not delete files outside the root directory", func() {
err := rfs.CreateServerFile("/../ext-source.txt", "external content")
err = fs.Delete("../ext-source.txt")
g.Assert(err).IsNotNil()
g.Assert(errors.Is(err, os.ErrNotExist)).IsTrue()
})
g.It("does not allow the deletion of the root directory", func() {
err := fs.Delete("/")
g.Assert(err).IsNotNil()
g.Assert(err.Error()).Equal("cannot delete root server directory")
})
g.It("does not return an error if the target does not exist", func() {
err := fs.Delete("missing.txt")
g.Assert(err).IsNil()
st, err := rfs.StatServerFile("source.txt")
g.Assert(err).IsNil()
g.Assert(st.Name()).Equal("source.txt")
})
g.It("deletes files and subtracts their size from the disk usage", func() {
err := fs.Delete("source.txt")
g.Assert(err).IsNil()
_, err = rfs.StatServerFile("source.txt")
g.Assert(err).IsNotNil()
g.Assert(errors.Is(err, os.ErrNotExist)).IsTrue()
g.Assert(fs.diskUsed).Equal(int64(0))
})
g.It("deletes all items inside a directory if the directory is deleted", func() {
sources := []string{
"foo/source.txt",
"foo/bar/source.txt",
"foo/bar/baz/source.txt",
}
err := os.MkdirAll(filepath.Join(rfs.root, "/server/foo/bar/baz"), 0755)
g.Assert(err).IsNil()
for _, s := range sources {
err = rfs.CreateServerFile(s, "test content")
g.Assert(err).IsNil()
}
fs.diskUsed = int64(utf8.RuneCountInString("test content") * 3)
err = fs.Delete("foo")
g.Assert(err).IsNil()
g.Assert(fs.diskUsed).Equal(int64(0))
for _, s := range sources {
_, err = rfs.StatServerFile(s)
g.Assert(err).IsNotNil()
g.Assert(errors.Is(err, os.ErrNotExist)).IsTrue()
}
})
g.AfterEach(func() {
rfs.reset()
fs.diskUsed = 0
fs.diskLimit = 0
})
})
}