Add basic logic needed to correctly mount the VHD when initializing a server.
This commit is contained in:
parent
7fed6a68cb
commit
265f8a6b39
3
Makefile
3
Makefile
|
@ -4,6 +4,9 @@ build:
|
||||||
GOOS=linux GOARCH=amd64 go build -ldflags="-s -w" -gcflags "all=-trimpath=$(pwd)" -o build/wings_linux_amd64 -v wings.go
|
GOOS=linux GOARCH=amd64 go build -ldflags="-s -w" -gcflags "all=-trimpath=$(pwd)" -o build/wings_linux_amd64 -v wings.go
|
||||||
GOOS=linux GOARCH=arm64 go build -ldflags="-s -w" -gcflags "all=-trimpath=$(pwd)" -o build/wings_linux_arm64 -v wings.go
|
GOOS=linux GOARCH=arm64 go build -ldflags="-s -w" -gcflags "all=-trimpath=$(pwd)" -o build/wings_linux_arm64 -v wings.go
|
||||||
|
|
||||||
|
race:
|
||||||
|
go build -ldflags="-X github.com/pterodactyl/wings/system.Version=$(GIT_HEAD)" -race
|
||||||
|
|
||||||
debug:
|
debug:
|
||||||
go build -ldflags="-X github.com/pterodactyl/wings/system.Version=$(GIT_HEAD)"
|
go build -ldflags="-X github.com/pterodactyl/wings/system.Version=$(GIT_HEAD)"
|
||||||
sudo ./wings --debug --ignore-certificate-errors --config config.yml --pprof --pprof-block-rate 1
|
sudo ./wings --debug --ignore-certificate-errors --config config.yml --pprof --pprof-block-rate 1
|
||||||
|
|
|
@ -14,6 +14,7 @@ import (
|
||||||
"github.com/pterodactyl/wings/loggers/cli"
|
"github.com/pterodactyl/wings/loggers/cli"
|
||||||
"github.com/pterodactyl/wings/remote"
|
"github.com/pterodactyl/wings/remote"
|
||||||
"github.com/pterodactyl/wings/server"
|
"github.com/pterodactyl/wings/server"
|
||||||
|
"github.com/pterodactyl/wings/server/filesystem"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -55,7 +56,7 @@ func (m *MigrateVHDCommand) Run(ctx context.Context) error {
|
||||||
for _, s := range m.manager.All() {
|
for _, s := range m.manager.All() {
|
||||||
s.Log().Debug("starting migration of server contents to virtual disk...")
|
s.Log().Debug("starting migration of server contents to virtual disk...")
|
||||||
|
|
||||||
v := s.Filesystem().NewVHD()
|
v := vhd.New(s.DiskSpace(), filesystem.VirtualDiskPath(s.Id()), s.Filesystem().Path())
|
||||||
if err := v.Allocate(ctx); err != nil {
|
if err := v.Allocate(ctx); err != nil {
|
||||||
return errors.WithStackIf(err)
|
return errors.WithStackIf(err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,7 +38,7 @@ func New(ctx context.Context, manager *server.Manager, details ServerDetails) (*
|
||||||
|
|
||||||
// Create a new server instance using the configuration we wrote to the disk
|
// Create a new server instance using the configuration we wrote to the disk
|
||||||
// so that everything gets instantiated correctly on the struct.
|
// so that everything gets instantiated correctly on the struct.
|
||||||
s, err := manager.InitServer(c)
|
s, err := manager.InitServer(ctx, c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.WrapIf(err, "installer: could not init server instance")
|
return nil, errors.WrapIf(err, "installer: could not init server instance")
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,6 +43,7 @@ type CfgOption func(d *Disk) *Disk
|
||||||
|
|
||||||
// Disk represents the underlying virtual disk for the instance.
|
// Disk represents the underlying virtual disk for the instance.
|
||||||
type Disk struct {
|
type Disk struct {
|
||||||
|
// The total size of the disk allowed in bytes.
|
||||||
size int64
|
size int64
|
||||||
diskPath string
|
diskPath string
|
||||||
mountAt string
|
mountAt string
|
||||||
|
@ -51,8 +52,8 @@ type Disk struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// New returns a new Disk instance. The "size" parameter should be provided in
|
// New returns a new Disk instance. The "size" parameter should be provided in
|
||||||
// megabytes of space allowed for the disk. An additional slice of option
|
// bytes of space allowed for the disk. An additional slice of option callbacks
|
||||||
// callbacks can be provided to programatically swap out the underlying filesystem
|
// can be provided to programatically swap out the underlying filesystem
|
||||||
// implementation or the underlying command exection engine.
|
// implementation or the underlying command exection engine.
|
||||||
func New(size int64, diskPath string, mountAt string, opts ...func(*Disk)) *Disk {
|
func New(size int64, diskPath string, mountAt string, opts ...func(*Disk)) *Disk {
|
||||||
if diskPath == "" || mountAt == "" {
|
if diskPath == "" || mountAt == "" {
|
||||||
|
@ -181,7 +182,10 @@ func (d *Disk) Allocate(ctx context.Context) error {
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
return errors.Wrap(err, "vhd: failed to check for existence of root disk")
|
return errors.Wrap(err, "vhd: failed to check for existence of root disk")
|
||||||
}
|
}
|
||||||
cmd := d.commander(ctx, "fallocate", "-l", fmt.Sprintf("%dM", d.size), d.diskPath)
|
// We use 1024 as the multiplier for all of the disk space logic within the
|
||||||
|
// application. Passing "K" (/1024) is the same as "KiB" for fallocate, but
|
||||||
|
// is different than "KB" (/1000).
|
||||||
|
cmd := d.commander(ctx, "fallocate", "-l", fmt.Sprintf("%dK", d.size / 1024), d.diskPath)
|
||||||
if _, err := cmd.Output(); err != nil {
|
if _, err := cmd.Output(); err != nil {
|
||||||
msg := "vhd: failed to execute fallocate command"
|
msg := "vhd: failed to execute fallocate command"
|
||||||
if v, ok := err.(*exec.ExitError); ok {
|
if v, ok := err.(*exec.ExitError); ok {
|
||||||
|
|
|
@ -62,14 +62,14 @@ func newMockDisk(c CommanderProvider) *Disk {
|
||||||
if c != nil {
|
if c != nil {
|
||||||
w = c
|
w = c
|
||||||
}
|
}
|
||||||
return New(100, "/foo", "/bar", WithFs(afero.NewMemMapFs()), WithCommander(w))
|
return New(100 * 1024 * 1024, "/foo", "/bar", WithFs(afero.NewMemMapFs()), WithCommander(w))
|
||||||
}
|
}
|
||||||
|
|
||||||
func Test_New(t *testing.T) {
|
func Test_New(t *testing.T) {
|
||||||
t.Run("creates expected struct", func(t *testing.T) {
|
t.Run("creates expected struct", func(t *testing.T) {
|
||||||
d := New(100, "/foo", "/bar")
|
d := New(100 * 1024 * 1024, "/foo", "/bar")
|
||||||
assert.NotNil(t, d)
|
assert.NotNil(t, d)
|
||||||
assert.Equal(t, int64(100), d.size)
|
assert.Equal(t, int64(100 * 1024 * 1024), d.size)
|
||||||
assert.Equal(t, "/foo", d.diskPath)
|
assert.Equal(t, "/foo", d.diskPath)
|
||||||
assert.Equal(t, "/bar", d.mountAt)
|
assert.Equal(t, "/bar", d.mountAt)
|
||||||
|
|
||||||
|
@ -360,7 +360,7 @@ func TestDisk_Allocate(t *testing.T) {
|
||||||
output: func() ([]byte, error) {
|
output: func() ([]byte, error) {
|
||||||
called = true
|
called = true
|
||||||
assert.Equal(t, "fallocate", name)
|
assert.Equal(t, "fallocate", name)
|
||||||
assert.Equal(t, []string{"-l", "100M", "/foo"}, args)
|
assert.Equal(t, []string{"-l", "102400K", "/foo"}, args)
|
||||||
return nil, nil
|
return nil, nil
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ package filesystem
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
|
"context"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
|
@ -20,6 +21,7 @@ import (
|
||||||
ignore "github.com/sabhiram/go-gitignore"
|
ignore "github.com/sabhiram/go-gitignore"
|
||||||
|
|
||||||
"github.com/pterodactyl/wings/config"
|
"github.com/pterodactyl/wings/config"
|
||||||
|
"github.com/pterodactyl/wings/internal/vhd"
|
||||||
"github.com/pterodactyl/wings/system"
|
"github.com/pterodactyl/wings/system"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -30,6 +32,7 @@ type Filesystem struct {
|
||||||
diskUsed int64
|
diskUsed int64
|
||||||
diskCheckInterval time.Duration
|
diskCheckInterval time.Duration
|
||||||
denylist *ignore.GitIgnore
|
denylist *ignore.GitIgnore
|
||||||
|
vhd *vhd.Disk
|
||||||
|
|
||||||
// The maximum amount of disk space (in bytes) that this Filesystem instance can use.
|
// The maximum amount of disk space (in bytes) that this Filesystem instance can use.
|
||||||
diskLimit int64
|
diskLimit int64
|
||||||
|
@ -41,8 +44,9 @@ type Filesystem struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// New creates a new Filesystem instance for a given server.
|
// New creates a new Filesystem instance for a given server.
|
||||||
func New(root string, size int64, denylist []string) *Filesystem {
|
func New(uuid string, size int64, denylist []string) *Filesystem {
|
||||||
return &Filesystem{
|
root := filepath.Join(config.Get().System.Data, uuid)
|
||||||
|
fs := Filesystem{
|
||||||
root: root,
|
root: root,
|
||||||
diskLimit: size,
|
diskLimit: size,
|
||||||
diskCheckInterval: time.Duration(config.Get().System.DiskCheckInterval),
|
diskCheckInterval: time.Duration(config.Get().System.DiskCheckInterval),
|
||||||
|
@ -50,6 +54,16 @@ func New(root string, size int64, denylist []string) *Filesystem {
|
||||||
lookupInProgress: system.NewAtomicBool(false),
|
lookupInProgress: system.NewAtomicBool(false),
|
||||||
denylist: ignore.CompileIgnoreLines(denylist...),
|
denylist: ignore.CompileIgnoreLines(denylist...),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if config.Get().System.UseVirtualDisks {
|
||||||
|
fs.vhd = vhd.New(size, VirtualDiskPath(uuid), fs.root)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &fs
|
||||||
|
}
|
||||||
|
|
||||||
|
func VirtualDiskPath(uuid string) string {
|
||||||
|
return filepath.Join(config.Get().System.Data, ".vhd/", uuid+".img")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Path returns the root path for the Filesystem instance.
|
// Path returns the root path for the Filesystem instance.
|
||||||
|
@ -57,6 +71,25 @@ func (fs *Filesystem) Path() string {
|
||||||
return fs.root
|
return fs.root
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsVirtual returns true if the filesystem is currently using a virtual disk.
|
||||||
|
func (fs *Filesystem) IsVirtual() bool {
|
||||||
|
return fs.vhd != nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MountDisk will attempt to mount the underlying virtual disk for the server.
|
||||||
|
// If the disk is already mounted this is a no-op function. If the filesystem is
|
||||||
|
// not configured for virtual disks this function will panic.
|
||||||
|
func (fs *Filesystem) MountDisk(ctx context.Context) error {
|
||||||
|
if !fs.IsVirtual() {
|
||||||
|
panic(errors.New("filesystem: cannot call MountDisk on Filesystem instance without VHD present"))
|
||||||
|
}
|
||||||
|
err := fs.vhd.Mount(ctx)
|
||||||
|
if errors.Is(err, vhd.ErrFilesystemMounted) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return errors.WrapIf(err, "filesystem: failed to mount VHD")
|
||||||
|
}
|
||||||
|
|
||||||
// File returns a reader for a file instance as well as the stat information.
|
// File returns a reader for a file instance as well as the stat information.
|
||||||
func (fs *Filesystem) File(p string) (*os.File, Stat, error) {
|
func (fs *Filesystem) File(p string) (*os.File, Stat, error) {
|
||||||
cleaned, err := fs.SafePath(p)
|
cleaned, err := fs.SafePath(p)
|
||||||
|
|
|
@ -1,17 +0,0 @@
|
||||||
package filesystem
|
|
||||||
|
|
||||||
import (
|
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/pterodactyl/wings/config"
|
|
||||||
"github.com/pterodactyl/wings/internal/vhd"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (fs *Filesystem) NewVHD() *vhd.Disk {
|
|
||||||
parts := strings.Split(fs.root, "/")
|
|
||||||
disk := filepath.Join(config.Get().System.Data, ".disks/", parts[len(parts)-1]+".img")
|
|
||||||
|
|
||||||
return vhd.New(250, disk, fs.root)
|
|
||||||
// return vhd.New(fs.diskLimit/1024/1024, disk, fs.root)
|
|
||||||
}
|
|
|
@ -5,7 +5,6 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
|
||||||
"runtime"
|
"runtime"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
@ -184,7 +183,7 @@ func (m *Manager) ReadStates() (map[string]string, error) {
|
||||||
// InitServer initializes a server using a data byte array. This will be
|
// InitServer initializes a server using a data byte array. This will be
|
||||||
// marshaled into the given struct using a YAML marshaler. This will also
|
// marshaled into the given struct using a YAML marshaler. This will also
|
||||||
// configure the given environment for a server.
|
// configure the given environment for a server.
|
||||||
func (m *Manager) InitServer(data remote.ServerConfigurationResponse) (*Server, error) {
|
func (m *Manager) InitServer(ctx context.Context, data remote.ServerConfigurationResponse) (*Server, error) {
|
||||||
s, err := New(m.client)
|
s, err := New(m.client)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -196,7 +195,15 @@ func (m *Manager) InitServer(data remote.ServerConfigurationResponse) (*Server,
|
||||||
return nil, errors.WithStackIf(err)
|
return nil, errors.WithStackIf(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
s.fs = filesystem.New(filepath.Join(config.Get().System.Data, s.ID()), s.DiskSpace(), s.Config().Egg.FileDenylist)
|
s.fs = filesystem.New(s.Id(), s.DiskSpace(), s.Config().Egg.FileDenylist)
|
||||||
|
// If this is a virtuakl filesystem we need to go ahead and mount the disk
|
||||||
|
// so that everything is accessible.
|
||||||
|
if s.fs.IsVirtual() {
|
||||||
|
log.WithField("server", s.Id()).Info("mounting virtual disk for server")
|
||||||
|
if err := s.fs.MountDisk(ctx); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Right now we only support a Docker based environment, so I'm going to hard code
|
// Right now we only support a Docker based environment, so I'm going to hard code
|
||||||
// this logic in. When we're ready to support other environment we'll need to make
|
// this logic in. When we're ready to support other environment we'll need to make
|
||||||
|
@ -258,7 +265,7 @@ func (m *Manager) init(ctx context.Context) error {
|
||||||
log.WithField("server", data.Uuid).WithField("error", err).Error("failed to parse server configuration from API response, skipping...")
|
log.WithField("server", data.Uuid).WithField("error", err).Error("failed to parse server configuration from API response, skipping...")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
s, err := m.InitServer(d)
|
s, err := m.InitServer(ctx, d)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.WithField("server", data.Uuid).WithField("error", err).Error("failed to load server, skipping...")
|
log.WithField("server", data.Uuid).WithField("error", err).Error("failed to load server, skipping...")
|
||||||
return
|
return
|
||||||
|
|
Loading…
Reference in New Issue
Block a user