wings/router/router_server_transfer.go
Ethan Alicea 4390bad36b
Please enter the commit message for your changes. Lines starting
with '#' will be ignored, and an empty message aborts the commit.

 Author:    Ethan Alicea <64653625+Tech-Gamer@users.noreply.github.com>

 On branch develop
 Your branch is up to date with 'origin/develop'.

 Changes to be committed:
	modified:   .github/workflows/push.yaml
	modified:   .github/workflows/release.yaml
	modified:   CHANGELOG.md
	modified:   Dockerfile
	modified:   Makefile
	modified:   README.md
	modified:   cmd/configure.go
	modified:   cmd/diagnostics.go
	modified:   cmd/root.go
	modified:   config/config.go
	modified:   environment/allocations.go
	modified:   environment/docker.go
	modified:   environment/docker/api.go
	modified:   environment/docker/container.go
	modified:   environment/docker/environment.go
	modified:   environment/docker/power.go
	modified:   environment/docker/stats.go
	modified:   environment/environment.go
	modified:   environment/settings.go
	modified:   events/events.go
	modified:   go.mod
	modified:   internal/cron/activity_cron.go
	modified:   internal/cron/cron.go
	modified:   internal/cron/sftp_cron.go
	modified:   internal/database/database.go
	modified:   internal/progress/progress.go
	modified:   internal/progress/progress_test.go
	modified:   loggers/cli/cli.go
	new file:   oryxBuildBinary
	modified:   parser/parser.go
	modified:   remote/http.go
	modified:   remote/servers.go
	modified:   remote/types.go
	modified:   router/downloader/downloader.go
	modified:   router/middleware.go
	modified:   router/middleware/middleware.go
	modified:   router/middleware/request_error.go
	modified:   router/router.go
	modified:   router/router_download.go
	modified:   router/router_server.go
	modified:   router/router_server_backup.go
	modified:   router/router_server_files.go
	modified:   router/router_server_transfer.go
	modified:   router/router_server_ws.go
	modified:   router/router_system.go
	modified:   router/router_transfer.go
	modified:   router/tokens/parser.go
	modified:   router/websocket/listeners.go
	modified:   router/websocket/websocket.go
	modified:   server/activity.go
	modified:   server/backup.go
	modified:   server/backup/backup.go
	modified:   server/backup/backup_local.go
	modified:   server/backup/backup_s3.go
	modified:   server/configuration.go
	modified:   server/console.go
	modified:   server/crash.go
	modified:   server/events.go
	modified:   server/filesystem/archive.go
	modified:   server/filesystem/filesystem.go
	modified:   server/filesystem/filesystem_test.go
	modified:   server/install.go
	modified:   server/installer/installer.go
	modified:   server/listeners.go
	modified:   server/manager.go
	modified:   server/mounts.go
	modified:   server/power.go
	modified:   server/power_test.go
	modified:   server/resources.go
	modified:   server/server.go
	modified:   server/transfer/archive.go
	modified:   server/transfer/source.go
	modified:   server/transfer/transfer.go
	modified:   server/update.go
	modified:   sftp/event.go
	modified:   sftp/handler.go
	modified:   sftp/server.go
	modified:   wings.go
2023-09-11 17:22:09 +00:00

131 lines
3.6 KiB
Go

package router
import (
"context"
"net/http"
"strings"
"time"
"emperror.dev/errors"
"github.com/gin-gonic/gin"
"github.com/Tech-Gamer/nwy-wings/environment"
"github.com/Tech-Gamer/nwy-wings/router/middleware"
"github.com/Tech-Gamer/nwy-wings/server"
"github.com/Tech-Gamer/nwy-wings/server/installer"
"github.com/Tech-Gamer/nwy-wings/server/transfer"
)
// Data passed over to initiate a server transfer.
type serverTransferRequest struct {
URL string `binding:"required" json:"url"`
Token string `binding:"required" json:"token"`
Server installer.ServerDetails `json:"server"`
}
// postServerTransfer handles the start of a transfer for a server.
func postServerTransfer(c *gin.Context) {
var data serverTransferRequest
if err := c.BindJSON(&data); err != nil {
return
}
s := ExtractServer(c)
// Check if the server is already being transferred.
// There will be another endpoint for resetting this value either by deleting the
// server, or by canceling the transfer.
if s.IsTransferring() {
c.AbortWithStatusJSON(http.StatusConflict, gin.H{
"error": "A transfer is already in progress for this server.",
})
return
}
manager := middleware.ExtractManager(c)
notifyPanelOfFailure := func() {
if err := manager.Client().SetTransferStatus(context.Background(), s.ID(), false); err != nil {
s.Log().WithField("subsystem", "transfer").
WithField("status", false).
WithError(err).
Error("failed to set transfer status")
}
s.Events().Publish(server.TransferStatusEvent, "failure")
s.SetTransferring(false)
}
// Block the server from starting while we are transferring it.
s.SetTransferring(true)
// Ensure the server is offline. Sometimes a "No such container" error gets through
// which means the server is already stopped. We can ignore that.
if s.Environment.State() != environment.ProcessOfflineState {
if err := s.Environment.WaitForStop(
s.Context(),
time.Second*15,
false,
); err != nil && !strings.Contains(strings.ToLower(err.Error()), "no such container") {
s.SetTransferring(false)
middleware.CaptureAndAbort(c, errors.Wrap(err, "failed to stop server for transfer"))
return
}
}
// Create a new transfer instance for this server.
trnsfr := transfer.New(context.Background(), s)
transfer.Outgoing().Add(trnsfr)
go func() {
defer transfer.Outgoing().Remove(trnsfr)
if _, err := trnsfr.PushArchiveToTarget(data.URL, data.Token); err != nil {
notifyPanelOfFailure()
if err == context.Canceled {
trnsfr.Log().Debug("canceled")
trnsfr.SendMessage("Canceled.")
return
}
trnsfr.Log().WithError(err).Error("failed to push archive to target")
return
}
// DO NOT NOTIFY THE PANEL OF SUCCESS HERE. The only node that should send
// a success status is the destination node. When we send a failure status,
// the panel will automatically cancel the transfer and attempt to reset
// the server state on the destination node, we just need to make sure
// we clean up our statuses for failure.
trnsfr.Log().Debug("transfer complete")
}()
c.Status(http.StatusAccepted)
}
// deleteServerTransfer cancels an outgoing transfer for a server.
func deleteServerTransfer(c *gin.Context) {
s := ExtractServer(c)
if !s.IsTransferring() {
c.AbortWithStatusJSON(http.StatusConflict, gin.H{
"error": "Server is not currently being transferred.",
})
return
}
trnsfr := transfer.Outgoing().Get(s.ID())
if trnsfr == nil {
c.AbortWithStatusJSON(http.StatusConflict, gin.H{
"error": "Server is not currently being transferred.",
})
return
}
trnsfr.Cancel()
c.Status(http.StatusAccepted)
}