2020-07-19 23:27:55 +00:00
|
|
|
package server
|
|
|
|
|
|
|
|
import (
|
2021-01-08 23:14:56 +00:00
|
|
|
"context"
|
2020-10-17 19:06:47 +00:00
|
|
|
"encoding/json"
|
2020-08-20 02:20:46 +00:00
|
|
|
"fmt"
|
2021-01-10 01:22:39 +00:00
|
|
|
"os"
|
|
|
|
"path/filepath"
|
|
|
|
"runtime"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"emperror.dev/errors"
|
2020-07-19 23:27:55 +00:00
|
|
|
"github.com/apex/log"
|
2020-08-01 04:40:43 +00:00
|
|
|
"github.com/gammazero/workerpool"
|
2020-07-19 23:27:55 +00:00
|
|
|
"github.com/pterodactyl/wings/api"
|
2020-09-27 19:24:08 +00:00
|
|
|
"github.com/pterodactyl/wings/config"
|
2020-08-11 04:38:42 +00:00
|
|
|
"github.com/pterodactyl/wings/environment"
|
|
|
|
"github.com/pterodactyl/wings/environment/docker"
|
2021-01-08 23:14:56 +00:00
|
|
|
"github.com/pterodactyl/wings/panelapi"
|
2020-09-27 19:24:08 +00:00
|
|
|
"github.com/pterodactyl/wings/server/filesystem"
|
2020-07-19 23:27:55 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// Iterates over a given directory and loads all of the servers listed before returning
|
|
|
|
// them to the calling function.
|
2021-01-08 23:14:56 +00:00
|
|
|
func (m *manager) Initialize(serversPerPage int) error {
|
|
|
|
if len(m.servers.items) != 0 {
|
2020-07-19 23:27:55 +00:00
|
|
|
return errors.New("cannot call LoadDirectory with a non-nil collection")
|
|
|
|
}
|
|
|
|
|
2020-08-20 02:20:46 +00:00
|
|
|
log.Info("fetching list of servers from API")
|
2021-01-08 23:14:56 +00:00
|
|
|
assignedServers, err := m.panelClient.GetServers(context.TODO(), serversPerPage)
|
2020-10-31 17:04:20 +00:00
|
|
|
if err != nil {
|
2021-01-08 23:14:56 +00:00
|
|
|
if !panelapi.IsRequestError(err) {
|
2020-11-28 23:57:10 +00:00
|
|
|
return err
|
2020-07-19 23:27:55 +00:00
|
|
|
}
|
|
|
|
|
2020-10-31 17:04:20 +00:00
|
|
|
return errors.New(err.Error())
|
2020-07-19 23:27:55 +00:00
|
|
|
}
|
|
|
|
|
2020-08-20 02:20:46 +00:00
|
|
|
start := time.Now()
|
2021-01-08 23:14:56 +00:00
|
|
|
log.WithField("total_configs", len(assignedServers)).Info("processing servers returned by the API")
|
2020-07-19 23:27:55 +00:00
|
|
|
|
2020-08-01 04:40:43 +00:00
|
|
|
pool := workerpool.New(runtime.NumCPU())
|
2020-10-31 18:13:40 +00:00
|
|
|
log.Debugf("using %d workerpools to instantiate server instances", runtime.NumCPU())
|
2021-01-08 23:14:56 +00:00
|
|
|
for _, data := range assignedServers {
|
2020-08-01 04:40:43 +00:00
|
|
|
pool.Submit(func() {
|
2020-10-17 19:06:47 +00:00
|
|
|
// 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
|
|
|
|
// messaging in the output.
|
2020-10-31 18:13:40 +00:00
|
|
|
d := api.ServerConfigurationResponse{
|
|
|
|
Settings: data.Settings,
|
|
|
|
}
|
2020-10-17 19:06:47 +00:00
|
|
|
|
2020-10-31 18:13:40 +00:00
|
|
|
log.WithField("server", data.Uuid).Info("creating new server object from API response")
|
|
|
|
if err := json.Unmarshal(data.ProcessConfiguration, &d.ProcessConfiguration); err != nil {
|
|
|
|
log.WithField("server", data.Uuid).WithField("error", err).Error("failed to parse server configuration from API response, skipping...")
|
2020-10-17 19:06:47 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
s, err := FromConfiguration(d)
|
2020-07-19 23:27:55 +00:00
|
|
|
if err != nil {
|
2020-10-31 18:13:40 +00:00
|
|
|
log.WithField("server", data.Uuid).WithField("error", err).Error("failed to load server, skipping...")
|
2020-07-19 23:27:55 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2021-01-08 23:14:56 +00:00
|
|
|
m.Add(s)
|
2020-08-01 04:40:43 +00:00
|
|
|
})
|
2020-07-19 23:27:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Wait until we've processed all of the configuration files in the directory
|
|
|
|
// before continuing.
|
2020-08-01 04:40:43 +00:00
|
|
|
pool.StopWait()
|
2020-07-19 23:27:55 +00:00
|
|
|
|
2020-08-20 02:20:46 +00:00
|
|
|
diff := time.Now().Sub(start)
|
|
|
|
log.WithField("duration", fmt.Sprintf("%s", diff)).Info("finished processing server configurations")
|
|
|
|
|
2020-07-19 23:27:55 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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.
|
2020-10-17 19:06:47 +00:00
|
|
|
func FromConfiguration(data api.ServerConfigurationResponse) (*Server, error) {
|
2020-12-25 19:21:09 +00:00
|
|
|
s, err := New()
|
|
|
|
if err != nil {
|
|
|
|
return nil, errors.WithMessage(err, "loader: failed to instantiate empty server struct")
|
2020-08-20 02:08:15 +00:00
|
|
|
}
|
2020-08-28 04:08:33 +00:00
|
|
|
if err := s.UpdateDataStructure(data.Settings); err != nil {
|
2020-07-19 23:27:55 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2020-08-11 04:38:42 +00:00
|
|
|
s.Archiver = Archiver{Server: s}
|
2020-09-27 19:24:08 +00:00
|
|
|
s.fs = filesystem.New(filepath.Join(config.Get().System.Data, s.Id()), s.DiskSpace())
|
2020-07-19 23:27:55 +00:00
|
|
|
|
|
|
|
// 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.
|
2020-08-28 04:08:33 +00:00
|
|
|
settings := environment.Settings{
|
|
|
|
Mounts: s.Mounts(),
|
|
|
|
Allocations: s.cfg.Allocations,
|
|
|
|
Limits: s.cfg.Build,
|
|
|
|
}
|
|
|
|
|
|
|
|
envCfg := environment.NewConfiguration(settings, s.GetEnvironmentVariables())
|
2020-08-11 04:38:42 +00:00
|
|
|
meta := docker.Metadata{
|
2020-09-01 03:27:41 +00:00
|
|
|
Image: s.Config().Container.Image,
|
2020-07-19 23:27:55 +00:00
|
|
|
}
|
|
|
|
|
2020-08-11 04:38:42 +00:00
|
|
|
if env, err := docker.New(s.Id(), &meta, envCfg); err != nil {
|
|
|
|
return nil, err
|
|
|
|
} else {
|
|
|
|
s.Environment = env
|
2020-09-12 06:01:54 +00:00
|
|
|
s.StartEventListeners()
|
2020-12-25 19:21:09 +00:00
|
|
|
s.Throttler().StartTimer(s.Context())
|
2020-07-31 23:01:02 +00:00
|
|
|
}
|
|
|
|
|
2020-07-19 23:27:55 +00:00
|
|
|
// Forces the configuration to be synced with the panel.
|
2020-07-20 00:26:53 +00:00
|
|
|
if err := s.SyncWithConfiguration(data); err != nil {
|
|
|
|
return nil, err
|
2020-07-19 23:27:55 +00:00
|
|
|
}
|
|
|
|
|
2020-08-06 03:13:01 +00:00
|
|
|
// If the server's data directory exists, force disk usage calculation.
|
2020-09-27 19:24:08 +00:00
|
|
|
if _, err := os.Stat(s.Filesystem().Path()); err == nil {
|
|
|
|
s.Filesystem().HasSpaceAvailable(true)
|
2020-08-06 03:13:01 +00:00
|
|
|
}
|
|
|
|
|
2020-07-19 23:27:55 +00:00
|
|
|
return s, nil
|
|
|
|
}
|