Nuke more API code and begin consolidation process

This commit is contained in:
Dane Everitt 2021-02-01 20:50:23 -08:00
parent aa287d21cf
commit 6775c17324
No known key found for this signature in database
GPG Key ID: EEA66103B3D71F53
5 changed files with 103 additions and 88 deletions

View File

@ -11,9 +11,9 @@ import (
type Client interface { type Client interface {
GetBackupRemoteUploadURLs(ctx context.Context, backup string, size int64) (api.BackupRemoteUploadResponse, error) GetBackupRemoteUploadURLs(ctx context.Context, backup string, size int64) (api.BackupRemoteUploadResponse, error)
GetInstallationScript(ctx context.Context, uuid string) (api.InstallationScript, error) GetInstallationScript(ctx context.Context, uuid string) (InstallationScript, error)
GetServerConfiguration(ctx context.Context, uuid string) (api.ServerConfigurationResponse, error) GetServerConfiguration(ctx context.Context, uuid string) (ServerConfigurationResponse, error)
GetServers(context context.Context, perPage int) ([]api.RawServerData, error) GetServers(context context.Context, perPage int) ([]RawServerData, error)
SetArchiveStatus(ctx context.Context, uuid string, successful bool) error SetArchiveStatus(ctx context.Context, uuid string, successful bool) error
SetBackupStatus(ctx context.Context, backup string, data api.BackupRequest) error SetBackupStatus(ctx context.Context, backup string, data api.BackupRequest) error
SetInstallationStatus(ctx context.Context, uuid string, successful bool) error SetInstallationStatus(ctx context.Context, uuid string, successful bool) error

View File

@ -13,13 +13,6 @@ import (
"github.com/pterodactyl/wings/system" "github.com/pterodactyl/wings/system"
) )
// Response is a custom response type that allows for commonly used error
// handling and response parsing from the Panel API. This just embeds the normal
// HTTP response from Go and we attach a few helper functions to it.
type Response struct {
*http.Response
}
// A generic type allowing for easy binding use when making requests to API // A generic type allowing for easy binding use when making requests to API
// endpoints that only expect a singular argument or something that would not // endpoints that only expect a singular argument or something that would not
// benefit from being a typed struct. // benefit from being a typed struct.
@ -30,6 +23,22 @@ type d map[string]interface{}
// Same concept as d, but a map of strings, used for querying GET requests. // Same concept as d, but a map of strings, used for querying GET requests.
type q map[string]string type q map[string]string
// Response is a custom response type that allows for commonly used error
// handling and response parsing from the Panel API. This just embeds the normal
// HTTP response from Go and we attach a few helper functions to it.
type Response struct {
*http.Response
}
type Pagination struct {
CurrentPage uint `json:"current_page"`
From uint `json:"from"`
LastPage uint `json:"last_page"`
PerPage uint `json:"per_page"`
To uint `json:"to"`
Total uint `json:"total"`
}
// requestOnce creates a http request and executes it once. Prefer request() // requestOnce creates a http request and executes it once. Prefer request()
// over this method when possible. It appends the path to the endpoint of the // over this method when possible. It appends the path to the endpoint of the
// client and adds the authentication token to the request. // client and adds the authentication token to the request.

View File

@ -51,7 +51,7 @@ type RawServerData struct {
// GetServers returns all of the servers that are present on the Panel making // GetServers returns all of the servers that are present on the Panel making
// parallel API calls to the endpoint if more than one page of servers is // parallel API calls to the endpoint if more than one page of servers is
// returned. // returned.
func (c *client) GetServers(ctx context.Context, limit int) ([]api.RawServerData, error) { func (c *client) GetServers(ctx context.Context, limit int) ([]RawServerData, error) {
servers, meta, err := c.getServersPaged(ctx, 0, limit) servers, meta, err := c.getServersPaged(ctx, 0, limit)
if err != nil { if err != nil {
return nil, err return nil, err
@ -81,34 +81,34 @@ func (c *client) GetServers(ctx context.Context, limit int) ([]api.RawServerData
return servers, nil return servers, nil
} }
func (c *client) GetServerConfiguration(ctx context.Context, uuid string) (api.ServerConfigurationResponse, error) { func (c *client) GetServerConfiguration(ctx context.Context, uuid string) (ServerConfigurationResponse, error) {
var config ServerConfigurationResponse
res, err := c.get(ctx, fmt.Sprintf("/servers/%s", uuid), nil) res, err := c.get(ctx, fmt.Sprintf("/servers/%s", uuid), nil)
if err != nil { if err != nil {
return api.ServerConfigurationResponse{}, err return config, err
} }
defer res.Body.Close() defer res.Body.Close()
if res.HasError() { if res.HasError() {
return api.ServerConfigurationResponse{}, err return config, err
} }
config := api.ServerConfigurationResponse{}
err = res.BindJSON(&config) err = res.BindJSON(&config)
return config, err return config, err
} }
func (c *client) GetInstallationScript(ctx context.Context, uuid string) (api.InstallationScript, error) { func (c *client) GetInstallationScript(ctx context.Context, uuid string) (InstallationScript, error) {
res, err := c.get(ctx, fmt.Sprintf("/servers/%s/install", uuid), nil) res, err := c.get(ctx, fmt.Sprintf("/servers/%s/install", uuid), nil)
if err != nil { if err != nil {
return api.InstallationScript{}, err return InstallationScript{}, err
} }
defer res.Body.Close() defer res.Body.Close()
if res.HasError() { if res.HasError() {
return api.InstallationScript{}, err return InstallationScript{}, err
} }
config := api.InstallationScript{} var config InstallationScript
err = res.BindJSON(&config) err = res.BindJSON(&config)
return config, err return config, err
} }
@ -146,7 +146,7 @@ func (c *client) SetTransferStatus(ctx context.Context, uuid string, successful
// getServersPaged returns a subset of servers from the Panel API using the // getServersPaged returns a subset of servers from the Panel API using the
// pagination query parameters. // pagination query parameters.
func (c *client) getServersPaged(ctx context.Context, page, limit int) ([]api.RawServerData, api.Pagination, error) { func (c *client) getServersPaged(ctx context.Context, page, limit int) ([]RawServerData, api.Pagination, error) {
res, err := c.get(ctx, "/servers", q{ res, err := c.get(ctx, "/servers", q{
"page": strconv.Itoa(page), "page": strconv.Itoa(page),
"per_page": strconv.Itoa(limit), "per_page": strconv.Itoa(limit),
@ -161,7 +161,7 @@ func (c *client) getServersPaged(ctx context.Context, page, limit int) ([]api.Ra
} }
var r struct { var r struct {
Data []api.RawServerData `json:"data"` Data []RawServerData `json:"data"`
Meta api.Pagination `json:"meta"` Meta api.Pagination `json:"meta"`
} }
if err := res.BindJSON(&r); err != nil { if err := res.BindJSON(&r); err != nil {

View File

@ -7,6 +7,7 @@ import (
"io" "io"
"io/ioutil" "io/ioutil"
"os" "os"
"path/filepath"
"runtime" "runtime"
"sync" "sync"
"time" "time"
@ -14,13 +15,16 @@ import (
"emperror.dev/errors" "emperror.dev/errors"
"github.com/apex/log" "github.com/apex/log"
"github.com/gammazero/workerpool" "github.com/gammazero/workerpool"
"github.com/pterodactyl/wings/api"
"github.com/pterodactyl/wings/config" "github.com/pterodactyl/wings/config"
"github.com/pterodactyl/wings/environment"
"github.com/pterodactyl/wings/environment/docker"
"github.com/pterodactyl/wings/remote" "github.com/pterodactyl/wings/remote"
"github.com/pterodactyl/wings/server/filesystem"
) )
type Manager struct { type Manager struct {
mu sync.RWMutex mu sync.RWMutex
client remote.Client
servers []*Server servers []*Server
} }
@ -28,8 +32,8 @@ type Manager struct {
// the servers that are currently present on the filesystem and set them into // the servers that are currently present on the filesystem and set them into
// the manager. // the manager.
func NewManager(ctx context.Context, client remote.Client) (*Manager, error) { func NewManager(ctx context.Context, client remote.Client) (*Manager, error) {
m := NewEmptyManager() m := NewEmptyManager(client)
if err := m.init(ctx, client); err != nil { if err := m.init(ctx); err != nil {
return nil, err return nil, err
} }
return m, nil return m, nil
@ -38,15 +42,15 @@ func NewManager(ctx context.Context, client remote.Client) (*Manager, error) {
// NewEmptyManager returns a new empty manager collection without actually // NewEmptyManager returns a new empty manager collection without actually
// loading any of the servers from the disk. This allows the caller to set their // loading any of the servers from the disk. This allows the caller to set their
// own servers into the collection as needed. // own servers into the collection as needed.
func NewEmptyManager() *Manager { func NewEmptyManager(client remote.Client) *Manager {
return &Manager{} return &Manager{client: client}
} }
// initializeFromRemoteSource iterates over a given directory and loads all of // initializeFromRemoteSource iterates over a given directory and loads all of
// the servers listed before returning them to the calling function. // the servers listed before returning them to the calling function.
func (m *Manager) init(ctx context.Context, client remote.Client) error { func (m *Manager) init(ctx context.Context) error {
log.Info("fetching list of servers from API") log.Info("fetching list of servers from API")
servers, err := client.GetServers(ctx, config.Get().RemoteQuery.BootServersPerPage) servers, err := m.client.GetServers(ctx, config.Get().RemoteQuery.BootServersPerPage)
if err != nil { if err != nil {
if !remote.IsRequestError(err) { if !remote.IsRequestError(err) {
return errors.WithStackIf(err) return errors.WithStackIf(err)
@ -65,7 +69,7 @@ func (m *Manager) init(ctx context.Context, client remote.Client) error {
// Parse the json.RawMessage into an expected struct value. We do this here so that a single broken // Parse the json.RawMessage into an expected struct value. We do this here so that a single broken
// server does not cause the entire boot process to hang, and allows us to show more useful error // server does not cause the entire boot process to hang, and allows us to show more useful error
// messaging in the output. // messaging in the output.
d := api.ServerConfigurationResponse{ d := remote.ServerConfigurationResponse{
Settings: data.Settings, Settings: data.Settings,
} }
log.WithField("server", data.Uuid).Info("creating new server object from API response") log.WithField("server", data.Uuid).Info("creating new server object from API response")
@ -73,7 +77,7 @@ func (m *Manager) init(ctx context.Context, client remote.Client) 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 := FromConfiguration(d) s, err := m.InitServer(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
@ -202,3 +206,53 @@ func (m *Manager) ReadStates() (map[string]string, error) {
} }
return out, nil return out, nil
} }
// InitServer 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 (m *Manager) InitServer(data remote.ServerConfigurationResponse) (*Server, error) {
s, err := New(m.client)
if err != nil {
return nil, errors.WithMessage(err, "loader: failed to instantiate empty server struct")
}
if err := s.UpdateDataStructure(data.Settings); err != nil {
return nil, err
}
s.Archiver = Archiver{Server: s}
s.fs = filesystem.New(filepath.Join(config.Get().System.Data, s.Id()), s.DiskSpace(), s.Config().Egg.FileDenylist)
// 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
// some modifications here obviously.
settings := environment.Settings{
Mounts: s.Mounts(),
Allocations: s.cfg.Allocations,
Limits: s.cfg.Build,
}
envCfg := environment.NewConfiguration(settings, s.GetEnvironmentVariables())
meta := docker.Metadata{
Image: s.Config().Container.Image,
}
if env, err := docker.New(s.Id(), &meta, envCfg); err != nil {
return nil, err
} else {
s.Environment = env
s.StartEventListeners()
s.Throttler().StartTimer(s.Context())
}
// Forces the configuration to be synced with the panel.
if err := s.SyncWithConfiguration(data); err != nil {
return nil, err
}
// If the server's data directory exists, force disk usage calculation.
if _, err := os.Stat(s.Filesystem().Path()); err == nil {
s.Filesystem().HasSpaceAvailable(true)
}
return s, nil
}

View File

@ -4,7 +4,6 @@ import (
"context" "context"
"fmt" "fmt"
"os" "os"
"path/filepath"
"strings" "strings"
"sync" "sync"
@ -16,6 +15,7 @@ import (
"github.com/pterodactyl/wings/environment" "github.com/pterodactyl/wings/environment"
"github.com/pterodactyl/wings/environment/docker" "github.com/pterodactyl/wings/environment/docker"
"github.com/pterodactyl/wings/events" "github.com/pterodactyl/wings/events"
"github.com/pterodactyl/wings/remote"
"github.com/pterodactyl/wings/server/filesystem" "github.com/pterodactyl/wings/server/filesystem"
"github.com/pterodactyl/wings/system" "github.com/pterodactyl/wings/system"
"golang.org/x/sync/semaphore" "golang.org/x/sync/semaphore"
@ -37,6 +37,7 @@ type Server struct {
// Maintains the configuration for the server. This is the data that gets returned by the Panel // Maintains the configuration for the server. This is the data that gets returned by the Panel
// such as build settings and container images. // such as build settings and container images.
cfg Configuration cfg Configuration
client remote.Client
// The crash handler for this server instance. // The crash handler for this server instance.
crasher CrashHandler crasher CrashHandler
@ -72,11 +73,12 @@ type Server struct {
// Returns a new server instance with a context and all of the default values set on // Returns a new server instance with a context and all of the default values set on
// the instance. // the instance.
func New() (*Server, error) { func New(client remote.Client) (*Server, error) {
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
s := Server{ s := Server{
ctx: ctx, ctx: ctx,
ctxCancel: &cancel, ctxCancel: &cancel,
client: client,
installing: system.NewAtomicBool(false), installing: system.NewAtomicBool(false),
transferring: system.NewAtomicBool(false), transferring: system.NewAtomicBool(false),
} }
@ -148,7 +150,7 @@ func (s *Server) Log() *log.Entry {
// This also means mass actions can be performed against servers on the Panel and they // This also means mass actions can be performed against servers on the Panel and they
// will automatically sync with Wings when the server is started. // will automatically sync with Wings when the server is started.
func (s *Server) Sync() error { func (s *Server) Sync() error {
cfg, err := api.New().GetServerConfiguration(s.Id()) cfg, err := s.client.GetServerConfiguration(s.Context(), s.Id())
if err != nil { if err != nil {
if !api.IsRequestError(err) { if !api.IsRequestError(err) {
return err return err
@ -164,7 +166,7 @@ func (s *Server) Sync() error {
return s.SyncWithConfiguration(cfg) return s.SyncWithConfiguration(cfg)
} }
func (s *Server) SyncWithConfiguration(cfg api.ServerConfigurationResponse) error { func (s *Server) SyncWithConfiguration(cfg remote.ServerConfigurationResponse) error {
// Update the data structure and persist it to the disk. // Update the data structure and persist it to the disk.
if err := s.UpdateDataStructure(cfg.Settings); err != nil { if err := s.UpdateDataStructure(cfg.Settings); err != nil {
return err return err
@ -295,61 +297,11 @@ func (s *Server) OnStateChange() {
} }
} }
// Determines if the server state is running or not. This is different than the // IsRunning determines if the server state is running or not. This is different
// environment state, it is simply the tracked state from this daemon instance, and // than the environment state, it is simply the tracked state from this daemon
// not the response from Docker. // instance, and not the response from Docker.
func (s *Server) IsRunning() bool { func (s *Server) IsRunning() bool {
st := s.Environment.State() st := s.Environment.State()
return st == environment.ProcessRunningState || st == environment.ProcessStartingState return st == environment.ProcessRunningState || st == environment.ProcessStartingState
} }
// FromConfiguration 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 api.ServerConfigurationResponse) (*Server, error) {
s, err := New()
if err != nil {
return nil, errors.WithMessage(err, "loader: failed to instantiate empty server struct")
}
if err := s.UpdateDataStructure(data.Settings); err != nil {
return nil, err
}
s.Archiver = Archiver{Server: s}
s.fs = filesystem.New(filepath.Join(config.Get().System.Data, s.Id()), s.DiskSpace(), s.Config().Egg.FileDenylist)
// 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
// some modifications here obviously.
settings := environment.Settings{
Mounts: s.Mounts(),
Allocations: s.cfg.Allocations,
Limits: s.cfg.Build,
}
envCfg := environment.NewConfiguration(settings, s.GetEnvironmentVariables())
meta := docker.Metadata{
Image: s.Config().Container.Image,
}
if env, err := docker.New(s.Id(), &meta, envCfg); err != nil {
return nil, err
} else {
s.Environment = env
s.StartEventListeners()
s.Throttler().StartTimer(s.Context())
}
// Forces the configuration to be synced with the panel.
if err := s.SyncWithConfiguration(data); err != nil {
return nil, err
}
// If the server's data directory exists, force disk usage calculation.
if _, err := os.Stat(s.Filesystem().Path()); err == nil {
s.Filesystem().HasSpaceAvailable(true)
}
return s, nil
}