2021-01-10 19:52:54 +00:00
|
|
|
package remote
|
2021-01-08 22:43:03 +00:00
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
|
|
|
"strconv"
|
|
|
|
"sync"
|
|
|
|
|
|
|
|
"github.com/pterodactyl/wings/api"
|
|
|
|
"golang.org/x/sync/errgroup"
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
ProcessStopCommand = "command"
|
|
|
|
ProcessStopSignal = "signal"
|
|
|
|
ProcessStopNativeStop = "stop"
|
|
|
|
)
|
|
|
|
|
2021-02-02 04:33:35 +00:00
|
|
|
// ServerConfigurationResponse 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.
|
2021-01-08 22:43:03 +00:00
|
|
|
//
|
2021-02-02 04:33:35 +00:00
|
|
|
// 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.
|
2021-01-08 22:43:03 +00:00
|
|
|
type ServerConfigurationResponse struct {
|
|
|
|
Settings json.RawMessage `json:"settings"`
|
|
|
|
ProcessConfiguration *api.ProcessConfiguration `json:"process_configuration"`
|
|
|
|
}
|
|
|
|
|
2021-02-02 04:33:35 +00:00
|
|
|
// InstallationScript 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.
|
2021-01-08 22:43:03 +00:00
|
|
|
type InstallationScript struct {
|
|
|
|
ContainerImage string `json:"container_image"`
|
|
|
|
Entrypoint string `json:"entrypoint"`
|
|
|
|
Script string `json:"script"`
|
|
|
|
}
|
|
|
|
|
2021-02-02 04:33:35 +00:00
|
|
|
// RawServerData is a raw response from the API for a server.
|
2021-01-08 22:43:03 +00:00
|
|
|
type RawServerData struct {
|
|
|
|
Uuid string `json:"uuid"`
|
|
|
|
Settings json.RawMessage `json:"settings"`
|
|
|
|
ProcessConfiguration json.RawMessage `json:"process_configuration"`
|
|
|
|
}
|
|
|
|
|
2021-02-02 04:26:15 +00:00
|
|
|
// GetServers returns all of the servers that are present on the Panel making
|
2021-02-02 04:33:35 +00:00
|
|
|
// parallel API calls to the endpoint if more than one page of servers is
|
|
|
|
// returned.
|
2021-02-02 04:26:15 +00:00
|
|
|
func (c *client) GetServers(ctx context.Context, limit int) ([]api.RawServerData, error) {
|
2021-02-02 04:33:35 +00:00
|
|
|
servers, meta, err := c.getServersPaged(ctx, 0, limit)
|
2021-01-08 22:43:03 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2021-02-02 04:26:15 +00:00
|
|
|
var mu sync.Mutex
|
|
|
|
if meta.LastPage > 1 {
|
|
|
|
g, ctx := errgroup.WithContext(ctx)
|
|
|
|
for page := meta.CurrentPage + 1; page <= meta.LastPage; page++ {
|
|
|
|
page := page
|
|
|
|
g.Go(func() error {
|
2021-02-02 04:33:35 +00:00
|
|
|
ps, _, err := c.getServersPaged(ctx, int(page), limit)
|
2021-01-08 22:43:03 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2021-02-02 04:26:15 +00:00
|
|
|
mu.Lock()
|
2021-01-08 22:43:03 +00:00
|
|
|
servers = append(servers, ps...)
|
2021-02-02 04:26:15 +00:00
|
|
|
mu.Unlock()
|
2021-01-08 22:43:03 +00:00
|
|
|
return nil
|
|
|
|
})
|
|
|
|
}
|
2021-02-02 04:26:15 +00:00
|
|
|
if err := g.Wait(); err != nil {
|
2021-01-08 22:43:03 +00:00
|
|
|
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"
|
|
|
|
}
|
2021-01-10 02:34:18 +00:00
|
|
|
resp, err := c.get(ctx, fmt.Sprintf("/servers/%s/transfer/%s", uuid, state), nil)
|
2021-01-08 22:43:03 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer resp.Body.Close()
|
|
|
|
return resp.Error()
|
|
|
|
}
|
2021-02-02 04:33:35 +00:00
|
|
|
|
|
|
|
// getServersPaged returns a subset of servers from the Panel API using the
|
|
|
|
// pagination query parameters.
|
|
|
|
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 struct {
|
|
|
|
Data []api.RawServerData `json:"data"`
|
|
|
|
Meta api.Pagination `json:"meta"`
|
|
|
|
}
|
|
|
|
if err := res.BindJSON(&r); err != nil {
|
|
|
|
return nil, api.Pagination{}, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return r.Data, r.Meta, nil
|
|
|
|
}
|