Merge pull request #16 from matthewpi/feature/server-transfers

Server Transfers (pterodactyl/panel#18)
This commit is contained in:
Dane Everitt
2020-04-05 11:06:34 -07:00
committed by GitHub
11 changed files with 541 additions and 47 deletions

104
server/archiver.go Normal file
View File

@@ -0,0 +1,104 @@
package server
import (
"crypto/sha256"
"encoding/hex"
"github.com/mholt/archiver/v3"
"github.com/pterodactyl/wings/config"
"io"
"io/ioutil"
"os"
"path/filepath"
)
// Archiver represents a Server Archiver.
type Archiver struct {
Server *Server
}
// ArchivePath returns the path to the server's archive.
func (a *Archiver) ArchivePath() string {
return filepath.Join(config.Get().System.ArchiveDirectory, a.ArchiveName())
}
// ArchiveName returns the name of the server's archive.
func (a *Archiver) ArchiveName() string {
return a.Server.Uuid + ".tar.gz"
}
// Exists returns a boolean based off if the archive exists.
func (a *Archiver) Exists() bool {
if _, err := os.Stat(a.ArchivePath()); os.IsNotExist(err) {
return false
}
return true
}
// Stat stats the archive file.
func (a *Archiver) Stat() (*Stat, error) {
return a.Server.Filesystem.unsafeStat(a.ArchivePath())
}
// Archive creates an archive of the server and deletes the previous one.
func (a *Archiver) Archive() error {
path := a.Server.Filesystem.Path()
// Get the list of root files and directories to archive.
var files []string
fileInfo, err := ioutil.ReadDir(path)
if err != nil {
return err
}
for _, file := range fileInfo {
files = append(files, filepath.Join(path, file.Name()))
}
stat, err := a.Stat()
if err != nil && !os.IsNotExist(err) {
return err
}
// Check if the file exists.
if stat != nil {
if err := os.Remove(a.ArchivePath()); err != nil {
return err
}
}
return archiver.NewTarGz().Archive(files, a.ArchivePath())
}
// DeleteIfExists deletes the archive if it exists.
func (a *Archiver) DeleteIfExists() error {
stat, err := a.Stat()
if err != nil && !os.IsNotExist(err) {
return err
}
// Check if the file exists.
if stat != nil {
if err := os.Remove(a.ArchivePath()); err != nil {
return err
}
}
return nil
}
// Checksum computes a SHA256 checksum of the server's archive.
func (a *Archiver) Checksum() (string, error) {
file, err := os.Open(a.ArchivePath())
if err != nil {
return "", err
}
defer file.Close()
hash := sha256.New()
if _, err := io.Copy(hash, file); err != nil {
return "", err
}
return hex.EncodeToString(hash.Sum(nil)), nil
}

View File

@@ -299,14 +299,18 @@ func (fs *Filesystem) Stat(p string) (*Stat, error) {
return nil, err
}
s, err := os.Stat(cleaned)
return fs.unsafeStat(cleaned)
}
func (fs *Filesystem) unsafeStat(p string) (*Stat, error) {
s, err := os.Stat(p)
if err != nil {
return nil, err
}
var m = "inode/directory"
if !s.IsDir() {
m, _, err = mimetype.DetectFile(cleaned)
m, _, err = mimetype.DetectFile(p)
if err != nil {
return nil, err
}

View File

@@ -27,11 +27,11 @@ func GetServers() *Collection {
// High level definition for a server instance being controlled by Wings.
type Server struct {
// The unique identifier for the server that should be used when referencing
// it aganist the Panel API (and internally). This will be used when naming
// it against the Panel API (and internally). This will be used when naming
// docker containers as well as in log output.
Uuid string `json:"uuid"`
// Wether or not the server is in a suspended state. Suspended servers cannot
// Whether or not the server is in a suspended state. Suspended servers cannot
// be started or modified except in certain scenarios by an admin user.
Suspended bool `json:"suspended"`
@@ -45,6 +45,7 @@ type Server struct {
// server process.
EnvVars map[string]string `json:"environment" yaml:"environment"`
Archiver Archiver `json:"-" yaml:"-"`
CrashDetection CrashDetection `json:"crash_detection" yaml:"crash_detection"`
Build BuildSettings `json:"build"`
Allocations Allocations `json:"allocations"`
@@ -205,7 +206,7 @@ func (s *Server) Init() {
s.mutex = &sync.Mutex{}
}
// Initalizes a server using a data byte array. This will be marshaled into the
// Initializes a server using a data byte array. This will be marshaled into the
// given struct using a YAML marshaler. This will also configure the given environment
// for a server.
func FromConfiguration(data []byte, cfg *config.SystemConfiguration) (*Server, error) {
@@ -231,6 +232,9 @@ func FromConfiguration(data []byte, cfg *config.SystemConfiguration) (*Server, e
}
s.Cache = cache.New(time.Minute*10, time.Minute*15)
s.Archiver = Archiver{
Server: s,
}
s.Filesystem = Filesystem{
Configuration: cfg,
Server: s,
@@ -367,7 +371,7 @@ func (s *Server) SetState(state string) error {
//
// In the event that we have passed the thresholds, don't do anything, otherwise
// automatically attempt to start the process back up for the user. This is done in a
// seperate thread as to not block any actions currently taking place in the flow
// separate thread as to not block any actions currently taking place in the flow
// that called this function.
if (prevState == ProcessStartingState || prevState == ProcessRunningState) && s.State == ProcessOfflineState {
zap.S().Infow("detected server as entering a potentially crashed state; running handler", zap.String("server", s.Uuid))

View File

@@ -6,7 +6,6 @@ import (
"github.com/imdario/mergo"
"github.com/pkg/errors"
"go.uber.org/zap"
"os"
)
// Merges data passed through in JSON form into the existing server object.
@@ -53,6 +52,11 @@ func (s *Server) UpdateDataStructure(data []byte, background bool) error {
}
} else {
s.Suspended = v
if s.Suspended {
zap.S().Debugw("server has been suspended", zap.String("server", s.Uuid))
} else {
zap.S().Debugw("server has been unsuspended", zap.String("server", s.Uuid))
}
}
// Environment and Mappings should be treated as a full update at all times, never a
@@ -101,12 +105,20 @@ func (s *Server) runBackgroundActions() {
if server.Suspended && server.State != ProcessOfflineState {
zap.S().Infow("server suspended with running process state, terminating now", zap.String("server", server.Uuid))
if err := server.Environment.Terminate(os.Kill); err != nil {
/*if err := server.Environment.Terminate(os.Kill); err != nil {
zap.S().Warnw(
"failed to terminate server environment after seeing suspension",
zap.String("server", server.Uuid),
zap.Error(err),
)
}*/
if err := server.Environment.WaitForStop(10, true); err != nil {
zap.S().Warnw(
"failed to stop server environment after seeing suspension",
zap.String("server", server.Uuid),
zap.Error(err),
)
}
}
}(s)