Merge branch 'dane/api-cleanup' into develop

This commit is contained in:
Dane Everitt
2021-02-23 21:25:10 -08:00
35 changed files with 616 additions and 1016 deletions

View File

@@ -13,6 +13,7 @@ import (
"github.com/gin-gonic/gin"
"github.com/google/uuid"
"github.com/pterodactyl/wings/config"
"github.com/pterodactyl/wings/remote"
"github.com/pterodactyl/wings/server"
"github.com/pterodactyl/wings/server/filesystem"
)
@@ -168,6 +169,15 @@ func AttachServerManager(m *server.Manager) gin.HandlerFunc {
}
}
// AttachApiClient attaches the application API client which allows routes to
// access server resources from the Panel easily.
func AttachApiClient(client remote.Client) gin.HandlerFunc {
return func(c *gin.Context) {
c.Set("api_client", client)
c.Next()
}
}
// CaptureAndAbort aborts the request and attaches the provided error to the gin
// context so it can be reported properly. If the error is missing a stacktrace
// at the time it is called the stack will be attached.
@@ -327,6 +337,14 @@ func ExtractServer(c *gin.Context) *server.Server {
return v.(*server.Server)
}
// ExtractApiClient returns the API client defined for the routes.
func ExtractApiClient(c *gin.Context) remote.Client {
if v, ok := c.Get("api_client"); ok {
return v.(remote.Client)
}
panic("middleware/middlware: cannot extract api clinet: not present in context")
}
// ExtractManager returns the server manager instance set on the request context.
func ExtractManager(c *gin.Context) *server.Manager {
if v, ok := c.Get("manager"); ok {

View File

@@ -3,18 +3,19 @@ package router
import (
"github.com/apex/log"
"github.com/gin-gonic/gin"
"github.com/pterodactyl/wings/remote"
"github.com/pterodactyl/wings/router/middleware"
"github.com/pterodactyl/wings/server"
)
// Configure configures the routing infrastructure for this daemon instance.
func Configure(m *server.Manager) *gin.Engine {
func Configure(m *server.Manager, client remote.Client) *gin.Engine {
gin.SetMode("release")
router := gin.New()
router.Use(gin.Recovery())
router.Use(middleware.AttachRequestID(), middleware.CaptureErrors(), middleware.SetAccessControlHeaders())
router.Use(middleware.AttachServerManager(m))
router.Use(middleware.AttachServerManager(m), middleware.AttachApiClient(client))
// @todo log this into a different file so you can setup IP blocking for abusive requests and such.
// This should still dump requests in debug mode since it does help with understanding the request
// lifecycle and quickly seeing what was called leading to the logs. However, it isn't feasible to mix

View File

@@ -15,6 +15,7 @@ import (
// Handle a download request for a server backup.
func getDownloadBackup(c *gin.Context) {
client := middleware.ExtractApiClient(c)
manager := middleware.ExtractManager(c)
token := tokens.BackupPayload{}
@@ -31,7 +32,7 @@ func getDownloadBackup(c *gin.Context) {
return
}
b, st, err := backup.LocateLocal(token.BackupUuid)
b, st, err := backup.LocateLocal(client, token.BackupUuid)
if err != nil {
if errors.Is(err, os.ErrNotExist) {
c.AbortWithStatusJSON(http.StatusNotFound, gin.H{

View File

@@ -17,6 +17,7 @@ import (
// provided backup adapter.
func postServerBackup(c *gin.Context) {
s := middleware.ExtractServer(c)
client := middleware.ExtractApiClient(c)
logger := middleware.ExtractLogger(c)
var data struct {
Adapter backup.AdapterType `json:"adapter"`
@@ -30,9 +31,9 @@ func postServerBackup(c *gin.Context) {
var adapter backup.BackupInterface
switch data.Adapter {
case backup.LocalBackupAdapter:
adapter = backup.NewLocal(data.Uuid, data.Ignore)
adapter = backup.NewLocal(client, data.Uuid, data.Ignore)
case backup.S3BackupAdapter:
adapter = backup.NewS3(data.Uuid, data.Ignore)
adapter = backup.NewS3(client, data.Uuid, data.Ignore)
default:
middleware.CaptureAndAbort(c, errors.New("router/backups: provided adapter is not valid: "+string(data.Adapter)))
return
@@ -65,6 +66,7 @@ func postServerBackup(c *gin.Context) {
// TODO: stop the server if it is running; internally mark it as suspended
func postServerRestoreBackup(c *gin.Context) {
s := middleware.ExtractServer(c)
client := middleware.ExtractApiClient(c)
logger := middleware.ExtractLogger(c)
var data struct {
@@ -94,7 +96,7 @@ func postServerRestoreBackup(c *gin.Context) {
// Now that we've cleaned up the data directory if necessary, grab the backup file
// and attempt to restore it into the server directory.
if data.Adapter == backup.LocalBackupAdapter {
b, _, err := backup.LocateLocal(c.Param("backup"))
b, _, err := backup.LocateLocal(client, c.Param("backup"))
if err != nil {
middleware.CaptureAndAbort(c, err)
return
@@ -114,7 +116,7 @@ func postServerRestoreBackup(c *gin.Context) {
// Since this is not a local backup we need to stream the archive and then
// parse over the contents as we go in order to restore it to the server.
client := http.Client{}
httpClient := http.Client{}
logger.Info("downloading backup from remote location...")
// TODO: this will hang if there is an issue. We can't use c.Request.Context() (or really any)
// since it will be canceled when the request is closed which happens quickly since we push
@@ -127,7 +129,7 @@ func postServerRestoreBackup(c *gin.Context) {
middleware.CaptureAndAbort(c, err)
return
}
res, err := client.Do(req)
res, err := httpClient.Do(req)
if err != nil {
middleware.CaptureAndAbort(c, err)
return
@@ -143,7 +145,7 @@ func postServerRestoreBackup(c *gin.Context) {
go func(s *server.Server, uuid string, logger *log.Entry) {
logger.Info("starting restoration process for server backup using S3 driver")
if err := s.RestoreBackup(backup.NewS3(uuid, ""), res.Body); err != nil {
if err := s.RestoreBackup(backup.NewS3(client, uuid, ""), res.Body); err != nil {
logger.WithField("error", errors.WithStack(err)).Error("failed to restore remote S3 backup to server")
}
s.Events().Publish(server.DaemonMessageEvent, "Completed server restoration from S3 backup.")
@@ -159,7 +161,7 @@ func postServerRestoreBackup(c *gin.Context) {
// endpoint can make its own decisions as to how it wants to handle that
// response.
func deleteServerBackup(c *gin.Context) {
b, _, err := backup.LocateLocal(c.Param("backup"))
b, _, err := backup.LocateLocal(middleware.ExtractApiClient(c), c.Param("backup"))
if err != nil {
// Just return from the function at this point if the backup was not located.
if errors.Is(err, os.ErrNotExist) {

View File

@@ -34,10 +34,11 @@ func getAllServers(c *gin.Context) {
// Creates a new server on the wings daemon and begins the installation process
// for it.
func postCreateServer(c *gin.Context) {
manager := middleware.ExtractManager(c)
buf := bytes.Buffer{}
buf.ReadFrom(c.Request.Body)
install, err := installer.New(buf.Bytes())
install, err := installer.New(c.Request.Context(), manager, buf.Bytes())
if err != nil {
if installer.IsValidationError(err) {
c.AbortWithStatusJSON(http.StatusUnprocessableEntity, gin.H{
@@ -52,7 +53,6 @@ func postCreateServer(c *gin.Context) {
// Plop that server instance onto the request so that it can be referenced in
// requests from here-on out.
manager := middleware.ExtractManager(c)
manager.Add(install.Server())
// Begin the installation process in the background to not block the request

View File

@@ -2,6 +2,7 @@ package router
import (
"bufio"
"context"
"crypto/sha256"
"encoding/hex"
"encoding/json"
@@ -22,9 +23,9 @@ import (
"github.com/juju/ratelimit"
"github.com/mholt/archiver/v3"
"github.com/mitchellh/colorstring"
"github.com/pterodactyl/wings/api"
"github.com/pterodactyl/wings/config"
"github.com/pterodactyl/wings/installer"
"github.com/pterodactyl/wings/remote"
"github.com/pterodactyl/wings/router/middleware"
"github.com/pterodactyl/wings/router/tokens"
"github.com/pterodactyl/wings/server"
@@ -109,10 +110,10 @@ func getServerArchive(c *gin.Context) {
}
func postServerArchive(c *gin.Context) {
s := ExtractServer(c)
s := middleware.ExtractServer(c)
manager := middleware.ExtractManager(c)
go func(s *server.Server) {
r := api.New()
l := log.WithField("server", s.Id())
// This function automatically adds the Source Node prefix and Timestamp to the log
@@ -133,12 +134,11 @@ func postServerArchive(c *gin.Context) {
// Mark the server as not being transferred so it can actually be used.
s.SetTransferring(false)
s.Events().Publish(server.TransferStatusEvent, "failure")
sendTransferLog("Attempting to notify panel of archive failure..")
if err := r.SendArchiveStatus(s.Id(), false); err != nil {
if !api.IsRequestError(err) {
if err := manager.Client().SetArchiveStatus(s.Context(), s.Id(), false); err != nil {
if !remote.IsRequestError(err) {
sendTransferLog("Failed to notify panel of archive failure: " + err.Error())
l.WithField("error", err).Error("failed to notify panel of failed archive status")
return
@@ -174,8 +174,8 @@ func postServerArchive(c *gin.Context) {
sendTransferLog("Successfully created archive, attempting to notify panel..")
l.Info("successfully created server transfer archive, notifying panel..")
if err := r.SendArchiveStatus(s.Id(), true); err != nil {
if !api.IsRequestError(err) {
if err := manager.Client().SetArchiveStatus(s.Context(), s.Id(), true); err != nil {
if !remote.IsRequestError(err) {
sendTransferLog("Failed to notify panel of archive success: " + err.Error())
l.WithField("error", err).Error("failed to notify panel of successful archive status")
return
@@ -275,10 +275,10 @@ func (str serverTransferRequest) verifyChecksum(matches string) (bool, string, e
}
// Sends a notification to the Panel letting it know what the status of this transfer is.
func (str serverTransferRequest) sendTransferStatus(successful bool) error {
func (str serverTransferRequest) sendTransferStatus(client remote.Client, successful bool) error {
lg := str.log().WithField("transfer_successful", successful)
lg.Info("notifying Panel of server transfer state")
if err := api.New().SendTransferStatus(str.ServerID, successful); err != nil {
if err := client.SetTransferStatus(context.Background(), str.ServerID, successful); err != nil {
lg.WithField("error", err).Error("error notifying panel of transfer state")
return err
}
@@ -294,6 +294,7 @@ func postTransfer(c *gin.Context) {
return
}
manager := middleware.ExtractManager(c)
u, err := uuid.Parse(data.ServerID)
if err != nil {
WithError(c, err)
@@ -310,9 +311,9 @@ func postTransfer(c *gin.Context) {
// Create a new server installer. This will only configure the environment and not
// run the installer scripts.
i, err := installer.New(data.Server)
i, err := installer.New(context.Background(), manager, data.Server)
if err != nil {
_ = data.sendTransferStatus(false)
_ = data.sendTransferStatus(manager.Client(), false)
data.log().WithField("error", err).Error("failed to validate received server data")
return
}
@@ -324,7 +325,6 @@ func postTransfer(c *gin.Context) {
i.Server().Events().Publish(server.TransferLogsEvent, output)
}
manager := middleware.ExtractManager(c)
// Mark the server as transferring to prevent problems later on during the process and
// then push the server into the global server collection for this instance.
i.Server().SetTransferring(true)
@@ -332,7 +332,7 @@ func postTransfer(c *gin.Context) {
defer func(s *server.Server) {
// In the event that this transfer call fails, remove the server from the global
// server tracking so that we don't have a dangling instance.
if err := data.sendTransferStatus(!hasError); hasError || err != nil {
if err := data.sendTransferStatus(manager.Client(), !hasError); hasError || err != nil {
sendTransferLog("Server transfer failed, check Wings logs for additional information.")
s.Events().Publish(server.TransferStatusEvent, "failure")
manager.Remove(func(match *server.Server) bool {