wings/remote/servers.go
2021-01-10 22:42:45 +01:00

168 lines
4.6 KiB
Go

package remote
import (
"context"
"encoding/json"
"fmt"
"strconv"
"sync"
"github.com/pterodactyl/wings/api"
"golang.org/x/sync/errgroup"
)
const (
ProcessStopCommand = "command"
ProcessStopSignal = "signal"
ProcessStopNativeStop = "stop"
)
// Holds the server configuration data returned from the Panel. When a server process
// is started, Wings communicates with the Panel to fetch the latest build information
// as well as get all of the details needed to parse the given Egg.
//
// This means we do not need to hit Wings each time part of the server is updated, and
// the Panel serves as the source of truth at all times. This also means if a configuration
// is accidentally wiped on Wings we can self-recover without too much hassle, so long
// as Wings is aware of what servers should exist on it.
type ServerConfigurationResponse struct {
Settings json.RawMessage `json:"settings"`
ProcessConfiguration *api.ProcessConfiguration `json:"process_configuration"`
}
// Defines installation script information for a server process. This is used when
// a server is installed for the first time, and when a server is marked for re-installation.
type InstallationScript struct {
ContainerImage string `json:"container_image"`
Entrypoint string `json:"entrypoint"`
Script string `json:"script"`
}
type allServerResponse struct {
Data []api.RawServerData `json:"data"`
Meta api.Pagination `json:"meta"`
}
type RawServerData struct {
Uuid string `json:"uuid"`
Settings json.RawMessage `json:"settings"`
ProcessConfiguration json.RawMessage `json:"process_configuration"`
}
func (c *client) GetServersPaged(ctx context.Context, page, limit int) ([]api.RawServerData, api.Pagination, error) {
res, err := c.get(ctx, "/servers", q{
"page": strconv.Itoa(page),
"per_page": strconv.Itoa(limit),
})
if err != nil {
return nil, api.Pagination{}, err
}
defer res.Body.Close()
if res.HasError() {
return nil, api.Pagination{}, res.Error()
}
var r allServerResponse
if err := res.BindJSON(&r); err != nil {
return nil, api.Pagination{}, err
}
return r.Data, r.Meta, nil
}
func (c *client) GetServers(ctx context.Context, perPage int) ([]api.RawServerData, error) {
servers, pageMeta, err := c.GetServersPaged(ctx, 0, perPage)
if err != nil {
return nil, err
}
// if the amount of servers exceeds the page limit, get the remaining pages in parallel
if pageMeta.LastPage > 1 {
eg, _ := errgroup.WithContext(ctx)
serversMu := sync.Mutex{}
for page := pageMeta.CurrentPage + 1; page <= pageMeta.LastPage; page++ {
eg.Go(func() error {
ps, _, err := c.GetServersPaged(ctx, perPage, int(page))
if err != nil {
return err
}
serversMu.Lock()
servers = append(servers, ps...)
serversMu.Unlock()
return nil
})
}
if err := eg.Wait(); err != nil {
return nil, err
}
}
return servers, nil
}
func (c *client) GetServerConfiguration(ctx context.Context, uuid string) (api.ServerConfigurationResponse, error) {
res, err := c.get(ctx, fmt.Sprintf("/servers/%s", uuid), nil)
if err != nil {
return api.ServerConfigurationResponse{}, err
}
defer res.Body.Close()
if res.HasError() {
return api.ServerConfigurationResponse{}, err
}
config := api.ServerConfigurationResponse{}
err = res.BindJSON(&config)
return config, err
}
func (c *client) GetInstallationScript(ctx context.Context, uuid string) (api.InstallationScript, error) {
res, err := c.get(ctx, fmt.Sprintf("/servers/%s/install", uuid), nil)
if err != nil {
return api.InstallationScript{}, err
}
defer res.Body.Close()
if res.HasError() {
return api.InstallationScript{}, err
}
config := api.InstallationScript{}
err = res.BindJSON(&config)
return config, err
}
func (c *client) SetInstallationStatus(ctx context.Context, uuid string, successful bool) error {
resp, err := c.post(ctx, fmt.Sprintf("/servers/%s/install", uuid), d{"successful": successful})
if err != nil {
return err
}
defer resp.Body.Close()
return resp.Error()
}
func (c *client) SetArchiveStatus(ctx context.Context, uuid string, successful bool) error {
resp, err := c.post(ctx, fmt.Sprintf("/servers/%s/archive", uuid), d{"successful": successful})
if err != nil {
return err
}
defer resp.Body.Close()
return resp.Error()
}
func (c *client) SetTransferStatus(ctx context.Context, uuid string, successful bool) error {
state := "failure"
if successful {
state = "success"
}
resp, err := c.get(ctx, fmt.Sprintf("/servers/%s/transfer/%s", uuid, state), nil)
if err != nil {
return err
}
defer resp.Body.Close()
return resp.Error()
}