Add ability to create archives of servers, add GET and POST /api/servers/:server/archive routes to get and request an archive of a server
This commit is contained in:
75
server/archiver.go
Normal file
75
server/archiver.go
Normal file
@@ -0,0 +1,75 @@
|
||||
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.Data, ".archives", 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 .
|
||||
func (a *Archiver) Stat() (*Stat, error) {
|
||||
return a.Server.Filesystem.unsafeStat(a.ArchivePath())
|
||||
}
|
||||
|
||||
// Archive creates an archive of the server.
|
||||
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()))
|
||||
}
|
||||
|
||||
return archiver.NewTarGz().Archive(files, a.ArchivePath())
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user