break everything
- upgrade dependencies - run gofmt and goimports to organize code - fix typos - other small tweaks
This commit is contained in:
parent
4a5e0bb86f
commit
3c54c1f840
|
@ -14,8 +14,9 @@ import (
|
||||||
|
|
||||||
"github.com/AlecAivazis/survey/v2"
|
"github.com/AlecAivazis/survey/v2"
|
||||||
"github.com/AlecAivazis/survey/v2/terminal"
|
"github.com/AlecAivazis/survey/v2/terminal"
|
||||||
"github.com/pterodactyl/wings/config"
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
|
"github.com/pterodactyl/wings/config"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
|
@ -21,11 +21,12 @@ import (
|
||||||
"github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types"
|
||||||
"github.com/docker/docker/pkg/parsers/kernel"
|
"github.com/docker/docker/pkg/parsers/kernel"
|
||||||
"github.com/docker/docker/pkg/parsers/operatingsystem"
|
"github.com/docker/docker/pkg/parsers/operatingsystem"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
"github.com/pterodactyl/wings/config"
|
"github.com/pterodactyl/wings/config"
|
||||||
"github.com/pterodactyl/wings/environment"
|
"github.com/pterodactyl/wings/environment"
|
||||||
"github.com/pterodactyl/wings/loggers/cli"
|
"github.com/pterodactyl/wings/loggers/cli"
|
||||||
"github.com/pterodactyl/wings/system"
|
"github.com/pterodactyl/wings/system"
|
||||||
"github.com/spf13/cobra"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const DefaultHastebinUrl = "https://ptero.co"
|
const DefaultHastebinUrl = "https://ptero.co"
|
||||||
|
|
29
cmd/root.go
29
cmd/root.go
|
@ -20,6 +20,10 @@ import (
|
||||||
"github.com/gammazero/workerpool"
|
"github.com/gammazero/workerpool"
|
||||||
"github.com/mitchellh/colorstring"
|
"github.com/mitchellh/colorstring"
|
||||||
"github.com/pkg/profile"
|
"github.com/pkg/profile"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"golang.org/x/crypto/acme"
|
||||||
|
"golang.org/x/crypto/acme/autocert"
|
||||||
|
|
||||||
"github.com/pterodactyl/wings/config"
|
"github.com/pterodactyl/wings/config"
|
||||||
"github.com/pterodactyl/wings/environment"
|
"github.com/pterodactyl/wings/environment"
|
||||||
"github.com/pterodactyl/wings/loggers/cli"
|
"github.com/pterodactyl/wings/loggers/cli"
|
||||||
|
@ -28,9 +32,6 @@ import (
|
||||||
"github.com/pterodactyl/wings/server"
|
"github.com/pterodactyl/wings/server"
|
||||||
"github.com/pterodactyl/wings/sftp"
|
"github.com/pterodactyl/wings/sftp"
|
||||||
"github.com/pterodactyl/wings/system"
|
"github.com/pterodactyl/wings/system"
|
||||||
"github.com/spf13/cobra"
|
|
||||||
"golang.org/x/crypto/acme"
|
|
||||||
"golang.org/x/crypto/acme/autocert"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -40,7 +41,7 @@ var (
|
||||||
|
|
||||||
var rootCommand = &cobra.Command{
|
var rootCommand = &cobra.Command{
|
||||||
Use: "wings",
|
Use: "wings",
|
||||||
Short: "Runs the API server allowing programatic control of game servers for Pterodactyl Panel.",
|
Short: "Runs the API server allowing programmatic control of game servers for Pterodactyl Panel.",
|
||||||
PreRun: func(cmd *cobra.Command, args []string) {
|
PreRun: func(cmd *cobra.Command, args []string) {
|
||||||
initConfig()
|
initConfig()
|
||||||
initLogging()
|
initLogging()
|
||||||
|
@ -90,9 +91,9 @@ func rootCmdRun(cmd *cobra.Command, _ []string) {
|
||||||
case "mem":
|
case "mem":
|
||||||
defer profile.Start(profile.MemProfile).Stop()
|
defer profile.Start(profile.MemProfile).Stop()
|
||||||
case "alloc":
|
case "alloc":
|
||||||
defer profile.Start(profile.MemProfile, profile.MemProfileAllocs()).Stop()
|
defer profile.Start(profile.MemProfile, profile.MemProfileAllocs).Stop()
|
||||||
case "heap":
|
case "heap":
|
||||||
defer profile.Start(profile.MemProfile, profile.MemProfileHeap()).Stop()
|
defer profile.Start(profile.MemProfile, profile.MemProfileHeap).Stop()
|
||||||
case "routines":
|
case "routines":
|
||||||
defer profile.Start(profile.GoroutineProfile).Stop()
|
defer profile.Start(profile.GoroutineProfile).Stop()
|
||||||
case "mutex":
|
case "mutex":
|
||||||
|
@ -159,7 +160,7 @@ func rootCmdRun(cmd *cobra.Command, _ []string) {
|
||||||
|
|
||||||
// Just for some nice log output.
|
// Just for some nice log output.
|
||||||
for _, s := range manager.All() {
|
for _, s := range manager.All() {
|
||||||
log.WithField("server", s.Id()).Info("finished loading configuration for server")
|
log.WithField("server", s.ID()).Info("finished loading configuration for server")
|
||||||
}
|
}
|
||||||
|
|
||||||
states, err := manager.ReadStates()
|
states, err := manager.ReadStates()
|
||||||
|
@ -201,14 +202,14 @@ func rootCmdRun(cmd *cobra.Command, _ []string) {
|
||||||
pool.Submit(func() {
|
pool.Submit(func() {
|
||||||
s.Log().Info("configuring server environment and restoring to previous state")
|
s.Log().Info("configuring server environment and restoring to previous state")
|
||||||
var st string
|
var st string
|
||||||
if state, exists := states[s.Id()]; exists {
|
if state, exists := states[s.ID()]; exists {
|
||||||
st = state
|
st = state
|
||||||
}
|
}
|
||||||
|
|
||||||
r, err := s.Environment.IsRunning()
|
r, err := s.Environment.IsRunning()
|
||||||
// We ignore missing containers because we don't want to actually block booting of wings at this
|
// We ignore missing containers because we don't want to actually block booting of wings at this
|
||||||
// point. If we didn't do this and you pruned all of the images and then started wings you could
|
// point. If we didn't do this, and you pruned all the images and then started wings you could
|
||||||
// end up waiting a long period of time for all of the images to be re-pulled on Wings boot rather
|
// end up waiting a long period of time for all the images to be re-pulled on Wings boot rather
|
||||||
// than when the server itself is started.
|
// than when the server itself is started.
|
||||||
if err != nil && !client.IsErrNotFound(err) {
|
if err != nil && !client.IsErrNotFound(err) {
|
||||||
s.Log().WithField("error", err).Error("error checking server environment status")
|
s.Log().WithField("error", err).Error("error checking server environment status")
|
||||||
|
@ -246,10 +247,10 @@ func rootCmdRun(cmd *cobra.Command, _ []string) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wait until all of the servers are ready to go before we fire up the SFTP and HTTP servers.
|
// Wait until all the servers are ready to go before we fire up the SFTP and HTTP servers.
|
||||||
pool.StopWait()
|
pool.StopWait()
|
||||||
defer func() {
|
defer func() {
|
||||||
// Cancel the context on all of the running servers at this point, even though the
|
// Cancel the context on all the running servers at this point, even though the
|
||||||
// program is just shutting down.
|
// program is just shutting down.
|
||||||
for _, s := range manager.All() {
|
for _, s := range manager.All() {
|
||||||
s.CtxCancel()
|
s.CtxCancel()
|
||||||
|
@ -266,7 +267,7 @@ func rootCmdRun(cmd *cobra.Command, _ []string) {
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
log.Info("updating server states on Panel: marking installing/restoring servers as normal")
|
log.Info("updating server states on Panel: marking installing/restoring servers as normal")
|
||||||
// Update all of the servers on the Panel to be in a valid state if they're
|
// Update all the servers on the Panel to be in a valid state if they're
|
||||||
// currently marked as installing/restoring now that Wings is restarted.
|
// currently marked as installing/restoring now that Wings is restarted.
|
||||||
if err := pclient.ResetServersState(cmd.Context()); err != nil {
|
if err := pclient.ResetServersState(cmd.Context()); err != nil {
|
||||||
log.WithField("error", err).Error("failed to reset server states on Panel: some instances may be stuck in an installing/restoring state unexpectedly")
|
log.WithField("error", err).Error("failed to reset server states on Panel: some instances may be stuck in an installing/restoring state unexpectedly")
|
||||||
|
@ -348,7 +349,7 @@ func rootCmdRun(cmd *cobra.Command, _ []string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reads the configuration from the disk and then sets up the global singleton
|
// Reads the configuration from the disk and then sets up the global singleton
|
||||||
// with all of the configuration values.
|
// with all the configuration values.
|
||||||
func initConfig() {
|
func initConfig() {
|
||||||
if !strings.HasPrefix(configPath, "/") {
|
if !strings.HasPrefix(configPath, "/") {
|
||||||
d, err := os.Getwd()
|
d, err := os.Getwd()
|
||||||
|
|
|
@ -21,8 +21,9 @@ import (
|
||||||
"github.com/cobaugh/osrelease"
|
"github.com/cobaugh/osrelease"
|
||||||
"github.com/creasty/defaults"
|
"github.com/creasty/defaults"
|
||||||
"github.com/gbrlsnchs/jwt/v3"
|
"github.com/gbrlsnchs/jwt/v3"
|
||||||
"github.com/pterodactyl/wings/system"
|
|
||||||
"gopkg.in/yaml.v2"
|
"gopkg.in/yaml.v2"
|
||||||
|
|
||||||
|
"github.com/pterodactyl/wings/system"
|
||||||
)
|
)
|
||||||
|
|
||||||
const DefaultLocation = "/etc/pterodactyl/config.yml"
|
const DefaultLocation = "/etc/pterodactyl/config.yml"
|
||||||
|
@ -53,7 +54,7 @@ var _jwtAlgo *jwt.HMACSHA
|
||||||
var _debugViaFlag bool
|
var _debugViaFlag bool
|
||||||
|
|
||||||
// Locker specific to writing the configuration to the disk, this happens
|
// Locker specific to writing the configuration to the disk, this happens
|
||||||
// in areas that might already be locked so we don't want to crash the process.
|
// in areas that might already be locked, so we don't want to crash the process.
|
||||||
var _writeLock sync.Mutex
|
var _writeLock sync.Mutex
|
||||||
|
|
||||||
// SftpConfiguration defines the configuration of the internal SFTP server.
|
// SftpConfiguration defines the configuration of the internal SFTP server.
|
||||||
|
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"github.com/docker/go-connections/nat"
|
"github.com/docker/go-connections/nat"
|
||||||
|
|
||||||
"github.com/pterodactyl/wings/config"
|
"github.com/pterodactyl/wings/config"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -19,7 +20,7 @@ type Allocations struct {
|
||||||
Port int `json:"port"`
|
Port int `json:"port"`
|
||||||
} `json:"default"`
|
} `json:"default"`
|
||||||
|
|
||||||
// Mappings contains all of the ports that should be assigned to a given server
|
// Mappings contains all the ports that should be assigned to a given server
|
||||||
// attached to the IP they correspond to.
|
// attached to the IP they correspond to.
|
||||||
Mappings map[string][]int `json:"mappings"`
|
Mappings map[string][]int `json:"mappings"`
|
||||||
}
|
}
|
||||||
|
@ -62,7 +63,7 @@ func (a *Allocations) DockerBindings() nat.PortMap {
|
||||||
iface := config.Get().Docker.Network.Interface
|
iface := config.Get().Docker.Network.Interface
|
||||||
|
|
||||||
out := a.Bindings()
|
out := a.Bindings()
|
||||||
// Loop over all of the bindings for this container, and convert any that reference 127.0.0.1
|
// Loop over all the bindings for this container, and convert any that reference 127.0.0.1
|
||||||
// to use the pterodactyl0 network interface IP, as that is the true local for what people are
|
// to use the pterodactyl0 network interface IP, as that is the true local for what people are
|
||||||
// trying to do when creating servers.
|
// trying to do when creating servers.
|
||||||
for p, binds := range out {
|
for p, binds := range out {
|
||||||
|
|
|
@ -10,6 +10,7 @@ import (
|
||||||
"github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types"
|
||||||
"github.com/docker/docker/api/types/network"
|
"github.com/docker/docker/api/types/network"
|
||||||
"github.com/docker/docker/client"
|
"github.com/docker/docker/client"
|
||||||
|
|
||||||
"github.com/pterodactyl/wings/config"
|
"github.com/pterodactyl/wings/config"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,7 @@ import (
|
||||||
"github.com/docker/docker/api/types/mount"
|
"github.com/docker/docker/api/types/mount"
|
||||||
"github.com/docker/docker/client"
|
"github.com/docker/docker/client"
|
||||||
"github.com/docker/docker/daemon/logger/jsonfilelog"
|
"github.com/docker/docker/daemon/logger/jsonfilelog"
|
||||||
|
|
||||||
"github.com/pterodactyl/wings/config"
|
"github.com/pterodactyl/wings/config"
|
||||||
"github.com/pterodactyl/wings/environment"
|
"github.com/pterodactyl/wings/environment"
|
||||||
"github.com/pterodactyl/wings/system"
|
"github.com/pterodactyl/wings/system"
|
||||||
|
@ -139,7 +140,7 @@ func (e *Environment) InSituUpdate() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create creates a new container for the server using all of the data that is
|
// Create creates a new container for the server using all the data that is
|
||||||
// currently available for it. If the container already exists it will be
|
// currently available for it. If the container already exists it will be
|
||||||
// returned.
|
// returned.
|
||||||
func (e *Environment) Create() error {
|
func (e *Environment) Create() error {
|
||||||
|
@ -192,7 +193,7 @@ func (e *Environment) Create() error {
|
||||||
PortBindings: a.DockerBindings(),
|
PortBindings: a.DockerBindings(),
|
||||||
|
|
||||||
// Configure the mounts for this container. First mount the server data directory
|
// Configure the mounts for this container. First mount the server data directory
|
||||||
// into the container as a r/w bind.
|
// into the container as an r/w bind.
|
||||||
Mounts: e.convertMounts(),
|
Mounts: e.convertMounts(),
|
||||||
|
|
||||||
// Configure the /tmp folder mapping in containers. This is necessary for some
|
// Configure the /tmp folder mapping in containers. This is necessary for some
|
||||||
|
@ -340,11 +341,9 @@ func (e *Environment) scanOutput(reader io.ReadCloser) {
|
||||||
|
|
||||||
events := e.Events()
|
events := e.Events()
|
||||||
|
|
||||||
err := system.ScanReader(reader, func(line string) {
|
if err := system.ScanReader(reader, func(line string) {
|
||||||
events.Publish(environment.ConsoleOutputEvent, line)
|
events.Publish(environment.ConsoleOutputEvent, line)
|
||||||
})
|
}); err != nil && err != io.EOF {
|
||||||
|
|
||||||
if err != nil && err != io.EOF {
|
|
||||||
log.WithField("error", err).WithField("container_id", e.Id).Warn("error processing scanner line in console output")
|
log.WithField("error", err).WithField("container_id", e.Id).Warn("error processing scanner line in console output")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -354,7 +353,7 @@ func (e *Environment) scanOutput(reader io.ReadCloser) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close the current reader before starting a new one, the defer will still run
|
// Close the current reader before starting a new one, the defer will still run,
|
||||||
// but it will do nothing if we already closed the stream.
|
// but it will do nothing if we already closed the stream.
|
||||||
_ = reader.Close()
|
_ = reader.Close()
|
||||||
|
|
||||||
|
@ -372,7 +371,7 @@ type imagePullStatus struct {
|
||||||
// error to the logger but continue with the process.
|
// error to the logger but continue with the process.
|
||||||
//
|
//
|
||||||
// The reasoning behind this is that Quay has had some serious outages as of
|
// The reasoning behind this is that Quay has had some serious outages as of
|
||||||
// late, and we don't need to block all of the servers from booting just because
|
// late, and we don't need to block all the servers from booting just because
|
||||||
// of that. I'd imagine in a lot of cases an outage shouldn't affect users too
|
// of that. I'd imagine in a lot of cases an outage shouldn't affect users too
|
||||||
// badly. It'll at least keep existing servers working correctly if anything.
|
// badly. It'll at least keep existing servers working correctly if anything.
|
||||||
func (e *Environment) ensureImageExists(image string) error {
|
func (e *Environment) ensureImageExists(image string) error {
|
||||||
|
|
|
@ -10,6 +10,7 @@ import (
|
||||||
"github.com/apex/log"
|
"github.com/apex/log"
|
||||||
"github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types"
|
||||||
"github.com/docker/docker/client"
|
"github.com/docker/docker/client"
|
||||||
|
|
||||||
"github.com/pterodactyl/wings/environment"
|
"github.com/pterodactyl/wings/environment"
|
||||||
"github.com/pterodactyl/wings/events"
|
"github.com/pterodactyl/wings/events"
|
||||||
"github.com/pterodactyl/wings/remote"
|
"github.com/pterodactyl/wings/remote"
|
||||||
|
@ -21,7 +22,7 @@ type Metadata struct {
|
||||||
Stop remote.ProcessStopConfiguration
|
Stop remote.ProcessStopConfiguration
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure that the Docker environment is always implementing all of the methods
|
// Ensure that the Docker environment is always implementing all the methods
|
||||||
// from the base environment interface.
|
// from the base environment interface.
|
||||||
var _ environment.ProcessEnvironment = (*Environment)(nil)
|
var _ environment.ProcessEnvironment = (*Environment)(nil)
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,7 @@ import (
|
||||||
"github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types"
|
||||||
"github.com/docker/docker/api/types/container"
|
"github.com/docker/docker/api/types/container"
|
||||||
"github.com/docker/docker/client"
|
"github.com/docker/docker/client"
|
||||||
|
|
||||||
"github.com/pterodactyl/wings/environment"
|
"github.com/pterodactyl/wings/environment"
|
||||||
"github.com/pterodactyl/wings/remote"
|
"github.com/pterodactyl/wings/remote"
|
||||||
)
|
)
|
||||||
|
@ -81,7 +82,7 @@ func (e *Environment) Start() error {
|
||||||
return e.Attach()
|
return e.Attach()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Truncate the log file so we don't end up outputting a bunch of useless log information
|
// Truncate the log file, so we don't end up outputting a bunch of useless log information
|
||||||
// to the websocket and whatnot. Check first that the path and file exist before trying
|
// to the websocket and whatnot. Check first that the path and file exist before trying
|
||||||
// to truncate them.
|
// to truncate them.
|
||||||
if _, err := os.Stat(c.LogPath); err == nil {
|
if _, err := os.Stat(c.LogPath); err == nil {
|
||||||
|
@ -242,7 +243,7 @@ func (e *Environment) Terminate(signal os.Signal) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
if !c.State.Running {
|
if !c.State.Running {
|
||||||
// If the container is not running but we're not already in a stopped state go ahead
|
// If the container is not running, but we're not already in a stopped state go ahead
|
||||||
// and update things to indicate we should be completely stopped now. Set to stopping
|
// and update things to indicate we should be completely stopped now. Set to stopping
|
||||||
// first so crash detection is not triggered.
|
// first so crash detection is not triggered.
|
||||||
if e.st.Load() != environment.ProcessOfflineState {
|
if e.st.Load() != environment.ProcessOfflineState {
|
||||||
|
|
|
@ -2,12 +2,14 @@ package docker
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"emperror.dev/errors"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"github.com/docker/docker/api/types"
|
|
||||||
"github.com/pterodactyl/wings/environment"
|
|
||||||
"io"
|
"io"
|
||||||
"math"
|
"math"
|
||||||
|
|
||||||
|
"emperror.dev/errors"
|
||||||
|
"github.com/docker/docker/api/types"
|
||||||
|
|
||||||
|
"github.com/pterodactyl/wings/environment"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Attach to the instance and then automatically emit an event whenever the resource usage for the
|
// Attach to the instance and then automatically emit an event whenever the resource usage for the
|
||||||
|
@ -73,9 +75,8 @@ func (e *Environment) pollResources(ctx context.Context) error {
|
||||||
// value which can be rather confusing to people trying to compare panel usage to
|
// value which can be rather confusing to people trying to compare panel usage to
|
||||||
// their stats output.
|
// their stats output.
|
||||||
//
|
//
|
||||||
// This math is straight up lifted from their CLI repository in order to show the same
|
// This math is from their CLI repository in order to show the same values to avoid people
|
||||||
// values to avoid people bothering me about it. It should also reflect a slightly more
|
// bothering me about it. It should also reflect a slightly more correct memory value anyways.
|
||||||
// correct memory value anyways.
|
|
||||||
//
|
//
|
||||||
// @see https://github.com/docker/cli/blob/96e1d1d6/cli/command/container/stats_helpers.go#L227-L249
|
// @see https://github.com/docker/cli/blob/96e1d1d6/cli/command/container/stats_helpers.go#L227-L249
|
||||||
func calculateDockerMemory(stats types.MemoryStats) uint64 {
|
func calculateDockerMemory(stats types.MemoryStats) uint64 {
|
||||||
|
|
|
@ -7,6 +7,7 @@ import (
|
||||||
|
|
||||||
"github.com/apex/log"
|
"github.com/apex/log"
|
||||||
"github.com/docker/docker/api/types/container"
|
"github.com/docker/docker/api/types/container"
|
||||||
|
|
||||||
"github.com/pterodactyl/wings/config"
|
"github.com/pterodactyl/wings/config"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -25,7 +26,7 @@ type Mount struct {
|
||||||
// that we're mounting into the container at the Target location.
|
// that we're mounting into the container at the Target location.
|
||||||
Source string `json:"source"`
|
Source string `json:"source"`
|
||||||
|
|
||||||
// Whether or not the directory is being mounted as read-only. It is up to the environment to
|
// Whether the directory is being mounted as read-only. It is up to the environment to
|
||||||
// handle this value correctly and ensure security expectations are met with its usage.
|
// handle this value correctly and ensure security expectations are met with its usage.
|
||||||
ReadOnly bool `json:"read_only"`
|
ReadOnly bool `json:"read_only"`
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,7 @@ type Stats struct {
|
||||||
|
|
||||||
// The total amount of memory this container or resource can use. Inside Docker this is
|
// The total amount of memory this container or resource can use. Inside Docker this is
|
||||||
// going to be higher than you'd expect because we're automatically allocating overhead
|
// going to be higher than you'd expect because we're automatically allocating overhead
|
||||||
// abilities for the container, so its not going to be a perfect match.
|
// abilities for the container, so it's not going to be a perfect match.
|
||||||
MemoryLimit uint64 `json:"memory_limit_bytes"`
|
MemoryLimit uint64 `json:"memory_limit_bytes"`
|
||||||
|
|
||||||
// The absolute CPU usage is the amount of CPU used in relation to the entire system and
|
// The absolute CPU usage is the amount of CPU used in relation to the entire system and
|
||||||
|
|
|
@ -30,7 +30,7 @@ func (e *EventBus) Publish(topic string, data string) {
|
||||||
// Some of our topics for the socket support passing a more specific namespace,
|
// Some of our topics for the socket support passing a more specific namespace,
|
||||||
// such as "backup completed:1234" to indicate which specific backup was completed.
|
// such as "backup completed:1234" to indicate which specific backup was completed.
|
||||||
//
|
//
|
||||||
// In these cases, we still need to the send the event using the standard listener
|
// In these cases, we still need to send the event using the standard listener
|
||||||
// name of "backup completed".
|
// name of "backup completed".
|
||||||
if strings.Contains(topic, ":") {
|
if strings.Contains(topic, ":") {
|
||||||
parts := strings.SplitN(topic, ":", 2)
|
parts := strings.SplitN(topic, ":", 2)
|
||||||
|
@ -43,7 +43,7 @@ func (e *EventBus) Publish(topic string, data string) {
|
||||||
e.mu.RLock()
|
e.mu.RLock()
|
||||||
defer e.mu.RUnlock()
|
defer e.mu.RUnlock()
|
||||||
|
|
||||||
// Acquire a read lock and loop over all of the channels registered for the topic. This
|
// Acquire a read lock and loop over all the channels registered for the topic. This
|
||||||
// avoids a panic crash if the process tries to unregister the channel while this routine
|
// avoids a panic crash if the process tries to unregister the channel while this routine
|
||||||
// is running.
|
// is running.
|
||||||
if cp, ok := e.pools[t]; ok {
|
if cp, ok := e.pools[t]; ok {
|
||||||
|
@ -65,7 +65,7 @@ func (e *EventBus) Publish(topic string, data string) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Publishes a JSON message to a given topic.
|
// PublishJson publishes a JSON message to a given topic.
|
||||||
func (e *EventBus) PublishJson(topic string, data interface{}) error {
|
func (e *EventBus) PublishJson(topic string, data interface{}) error {
|
||||||
b, err := json.Marshal(data)
|
b, err := json.Marshal(data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -77,7 +77,7 @@ func (e *EventBus) PublishJson(topic string, data interface{}) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Register a callback function that will be executed each time one of the events using the topic
|
// On adds a callback function that will be executed each time one of the events using the topic
|
||||||
// name is called.
|
// name is called.
|
||||||
func (e *EventBus) On(topic string, callback *func(Event)) {
|
func (e *EventBus) On(topic string, callback *func(Event)) {
|
||||||
e.mu.Lock()
|
e.mu.Lock()
|
||||||
|
@ -97,7 +97,7 @@ func (e *EventBus) On(topic string, callback *func(Event)) {
|
||||||
e.pools[topic].Add(callback)
|
e.pools[topic].Add(callback)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Removes an event listener from the bus.
|
// Off removes an event listener from the bus.
|
||||||
func (e *EventBus) Off(topic string, callback *func(Event)) {
|
func (e *EventBus) Off(topic string, callback *func(Event)) {
|
||||||
e.mu.Lock()
|
e.mu.Lock()
|
||||||
defer e.mu.Unlock()
|
defer e.mu.Unlock()
|
||||||
|
@ -107,7 +107,7 @@ func (e *EventBus) Off(topic string, callback *func(Event)) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Removes all of the event listeners that have been registered for any topic. Also stops the worker
|
// Destroy removes all the event listeners that have been registered for any topic. Also stops the worker
|
||||||
// pool to close that routine.
|
// pool to close that routine.
|
||||||
func (e *EventBus) Destroy() {
|
func (e *EventBus) Destroy() {
|
||||||
e.mu.Lock()
|
e.mu.Lock()
|
||||||
|
|
96
go.mod
96
go.mod
|
@ -4,79 +4,69 @@ go 1.16
|
||||||
|
|
||||||
require (
|
require (
|
||||||
emperror.dev/errors v0.8.0
|
emperror.dev/errors v0.8.0
|
||||||
github.com/AlecAivazis/survey/v2 v2.2.7
|
github.com/AlecAivazis/survey/v2 v2.2.15
|
||||||
github.com/Jeffail/gabs/v2 v2.6.0
|
github.com/Jeffail/gabs/v2 v2.6.1
|
||||||
github.com/Microsoft/go-winio v0.4.16 // indirect
|
github.com/Microsoft/go-winio v0.5.0 // indirect
|
||||||
github.com/Microsoft/hcsshim v0.8.14 // indirect
|
github.com/Microsoft/hcsshim v0.8.20 // indirect
|
||||||
github.com/NYTimes/logrotate v1.0.0
|
github.com/NYTimes/logrotate v1.0.0
|
||||||
github.com/andybalholm/brotli v1.0.1 // indirect
|
github.com/andybalholm/brotli v1.0.3 // indirect
|
||||||
github.com/apex/log v1.9.0
|
github.com/apex/log v1.9.0
|
||||||
github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef
|
github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d
|
||||||
github.com/beevik/etree v1.1.0
|
github.com/beevik/etree v1.1.0
|
||||||
github.com/buger/jsonparser v1.1.0
|
github.com/buger/jsonparser v1.1.1
|
||||||
github.com/cenkalti/backoff/v4 v4.1.0
|
github.com/cenkalti/backoff/v4 v4.1.1
|
||||||
github.com/cobaugh/osrelease v0.0.0-20181218015638-a93a0a55a249
|
github.com/cobaugh/osrelease v0.0.0-20181218015638-a93a0a55a249
|
||||||
github.com/containerd/containerd v1.4.3 // indirect
|
github.com/containerd/containerd v1.5.5 // indirect
|
||||||
github.com/containerd/fifo v0.0.0-20201026212402-0724c46b320c // indirect
|
|
||||||
github.com/creasty/defaults v1.5.1
|
github.com/creasty/defaults v1.5.1
|
||||||
github.com/docker/distribution v2.7.1+incompatible // indirect
|
github.com/docker/docker v20.10.7+incompatible
|
||||||
github.com/docker/docker v20.10.1+incompatible
|
|
||||||
github.com/docker/go-connections v0.4.0
|
github.com/docker/go-connections v0.4.0
|
||||||
github.com/docker/go-metrics v0.0.1 // indirect
|
github.com/fatih/color v1.12.0
|
||||||
github.com/fatih/color v1.10.0
|
|
||||||
github.com/franela/goblin v0.0.0-20200825194134-80c0062ed6cd
|
github.com/franela/goblin v0.0.0-20200825194134-80c0062ed6cd
|
||||||
github.com/fsnotify/fsnotify v1.4.9 // indirect
|
github.com/gabriel-vasile/mimetype v1.3.1
|
||||||
github.com/gabriel-vasile/mimetype v1.1.2
|
github.com/gammazero/workerpool v1.1.2
|
||||||
github.com/gammazero/deque v0.0.0-20201010052221-3932da5530cc // indirect
|
github.com/gbrlsnchs/jwt/v3 v3.0.1
|
||||||
github.com/gammazero/workerpool v1.1.1
|
github.com/gin-gonic/gin v1.7.2
|
||||||
github.com/gbrlsnchs/jwt/v3 v3.0.0
|
github.com/go-playground/validator/v10 v10.8.0 // indirect
|
||||||
github.com/gin-gonic/gin v1.6.3
|
github.com/golang/snappy v0.0.4 // indirect
|
||||||
github.com/go-playground/validator/v10 v10.4.1 // indirect
|
github.com/google/uuid v1.3.0
|
||||||
github.com/golang/snappy v0.0.2 // indirect
|
|
||||||
github.com/google/go-cmp v0.5.2 // indirect
|
|
||||||
github.com/google/uuid v1.1.2
|
|
||||||
github.com/gorilla/mux v1.7.4 // indirect
|
github.com/gorilla/mux v1.7.4 // indirect
|
||||||
github.com/gorilla/websocket v1.4.2
|
github.com/gorilla/websocket v1.4.2
|
||||||
github.com/iancoleman/strcase v0.1.2
|
github.com/iancoleman/strcase v0.2.0
|
||||||
github.com/icza/dyno v0.0.0-20200205103839-49cb13720835
|
github.com/icza/dyno v0.0.0-20210726202311-f1bafe5d9996
|
||||||
github.com/imdario/mergo v0.3.9
|
github.com/imdario/mergo v0.3.12
|
||||||
github.com/juju/ratelimit v1.0.1
|
github.com/juju/ratelimit v1.0.1
|
||||||
github.com/karrick/godirwalk v1.16.1
|
github.com/karrick/godirwalk v1.16.1
|
||||||
github.com/klauspost/compress v1.11.4 // indirect
|
github.com/klauspost/compress v1.13.2 // indirect
|
||||||
github.com/klauspost/pgzip v1.2.5
|
github.com/klauspost/pgzip v1.2.5
|
||||||
github.com/leodido/go-urn v1.2.1 // indirect
|
github.com/magefile/mage v1.11.0 // indirect
|
||||||
github.com/magefile/mage v1.10.0 // indirect
|
github.com/magiconair/properties v1.8.5
|
||||||
github.com/magiconair/properties v1.8.4
|
|
||||||
github.com/mattn/go-colorable v0.1.8
|
github.com/mattn/go-colorable v0.1.8
|
||||||
|
github.com/mattn/go-isatty v0.0.13 // indirect
|
||||||
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
|
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
|
||||||
github.com/mholt/archiver/v3 v3.5.0
|
github.com/mholt/archiver/v3 v3.5.0
|
||||||
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db
|
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db
|
||||||
github.com/moby/term v0.0.0-20201216013528-df9cb8a40635 // indirect
|
github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 // indirect
|
||||||
github.com/morikuni/aec v1.0.0 // indirect
|
github.com/morikuni/aec v1.0.0 // indirect
|
||||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
|
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
|
||||||
github.com/opencontainers/go-digest v1.0.0 // indirect
|
github.com/nwaples/rardecode v1.1.1 // indirect
|
||||||
github.com/opencontainers/image-spec v1.0.1 // indirect
|
|
||||||
github.com/patrickmn/go-cache v2.1.0+incompatible
|
github.com/patrickmn/go-cache v2.1.0+incompatible
|
||||||
github.com/pierrec/lz4/v4 v4.1.2 // indirect
|
github.com/pierrec/lz4/v4 v4.1.8 // indirect
|
||||||
github.com/pkg/profile v1.5.0
|
github.com/pkg/profile v1.6.0
|
||||||
github.com/pkg/sftp v1.12.0
|
github.com/pkg/sftp v1.13.2
|
||||||
github.com/prometheus/client_golang v1.9.0 // indirect
|
github.com/prometheus/common v0.30.0 // indirect
|
||||||
|
github.com/prometheus/procfs v0.7.1 // indirect
|
||||||
github.com/sabhiram/go-gitignore v0.0.0-20201211210132-54b8a0bf510f
|
github.com/sabhiram/go-gitignore v0.0.0-20201211210132-54b8a0bf510f
|
||||||
github.com/sirupsen/logrus v1.7.0 // indirect
|
github.com/spf13/cobra v1.2.1
|
||||||
github.com/spf13/cobra v1.1.1
|
github.com/stretchr/testify v1.7.0
|
||||||
github.com/stretchr/testify v1.6.1
|
github.com/ulikunitz/xz v0.5.10 // indirect
|
||||||
github.com/ugorji/go v1.2.2 // indirect
|
go.uber.org/atomic v1.9.0 // indirect
|
||||||
github.com/ulikunitz/xz v0.5.9 // indirect
|
go.uber.org/multierr v1.7.0 // indirect
|
||||||
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad
|
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97
|
||||||
golang.org/x/net v0.0.0-20201224014010-6772e930b67b // indirect
|
golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985 // indirect
|
||||||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
|
||||||
golang.org/x/sys v0.0.0-20201223074533-0d417f636930 // indirect
|
golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b // indirect
|
||||||
golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf // indirect
|
golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac // indirect
|
||||||
golang.org/x/text v0.3.4 // indirect
|
google.golang.org/genproto v0.0.0-20210729151513-df9385d47c1b // indirect
|
||||||
golang.org/x/time v0.0.0-20201208040808-7e3f01d25324 // indirect
|
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
|
|
||||||
google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d // indirect
|
|
||||||
google.golang.org/grpc v1.34.0 // indirect
|
|
||||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect
|
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect
|
||||||
gopkg.in/ini.v1 v1.62.0
|
gopkg.in/ini.v1 v1.62.0
|
||||||
gopkg.in/yaml.v2 v2.4.0
|
gopkg.in/yaml.v2 v2.4.0
|
||||||
|
|
|
@ -7,6 +7,7 @@ import (
|
||||||
"emperror.dev/errors"
|
"emperror.dev/errors"
|
||||||
"github.com/asaskevich/govalidator"
|
"github.com/asaskevich/govalidator"
|
||||||
"github.com/buger/jsonparser"
|
"github.com/buger/jsonparser"
|
||||||
|
|
||||||
"github.com/pterodactyl/wings/environment"
|
"github.com/pterodactyl/wings/environment"
|
||||||
"github.com/pterodactyl/wings/remote"
|
"github.com/pterodactyl/wings/remote"
|
||||||
"github.com/pterodactyl/wings/server"
|
"github.com/pterodactyl/wings/server"
|
||||||
|
@ -16,7 +17,7 @@ type Installer struct {
|
||||||
server *server.Server
|
server *server.Server
|
||||||
}
|
}
|
||||||
|
|
||||||
// New validates the received data to ensure that all of the required fields
|
// New validates the received data to ensure that all the required fields
|
||||||
// have been passed along in the request. This should be manually run before
|
// have been passed along in the request. This should be manually run before
|
||||||
// calling Execute().
|
// calling Execute().
|
||||||
func New(ctx context.Context, manager *server.Manager, data []byte) (*Installer, error) {
|
func New(ctx context.Context, manager *server.Manager, data []byte) (*Installer, error) {
|
||||||
|
@ -85,7 +86,7 @@ func New(ctx context.Context, manager *server.Manager, data []byte) (*Installer,
|
||||||
|
|
||||||
// Uuid returns the UUID associated with this installer instance.
|
// Uuid returns the UUID associated with this installer instance.
|
||||||
func (i *Installer) Uuid() string {
|
func (i *Installer) Uuid() string {
|
||||||
return i.server.Id()
|
return i.server.ID()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Server returns the server instance.
|
// Server returns the server instance.
|
||||||
|
|
|
@ -1,17 +1,18 @@
|
||||||
package cli
|
package cli
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"emperror.dev/errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/apex/log"
|
|
||||||
"github.com/apex/log/handlers/cli"
|
|
||||||
color2 "github.com/fatih/color"
|
|
||||||
"github.com/mattn/go-colorable"
|
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"emperror.dev/errors"
|
||||||
|
"github.com/apex/log"
|
||||||
|
"github.com/apex/log/handlers/cli"
|
||||||
|
color2 "github.com/fatih/color"
|
||||||
|
"github.com/mattn/go-colorable"
|
||||||
)
|
)
|
||||||
|
|
||||||
var Default = New(os.Stderr, true)
|
var Default = New(os.Stderr, true)
|
||||||
|
|
|
@ -15,9 +15,10 @@ import (
|
||||||
"github.com/buger/jsonparser"
|
"github.com/buger/jsonparser"
|
||||||
"github.com/icza/dyno"
|
"github.com/icza/dyno"
|
||||||
"github.com/magiconair/properties"
|
"github.com/magiconair/properties"
|
||||||
"github.com/pterodactyl/wings/config"
|
|
||||||
"gopkg.in/ini.v1"
|
"gopkg.in/ini.v1"
|
||||||
"gopkg.in/yaml.v2"
|
"gopkg.in/yaml.v2"
|
||||||
|
|
||||||
|
"github.com/pterodactyl/wings/config"
|
||||||
)
|
)
|
||||||
|
|
||||||
// The file parsing options that are available for a server configuration file.
|
// The file parsing options that are available for a server configuration file.
|
||||||
|
|
|
@ -15,6 +15,7 @@ import (
|
||||||
"emperror.dev/errors"
|
"emperror.dev/errors"
|
||||||
"github.com/apex/log"
|
"github.com/apex/log"
|
||||||
"github.com/cenkalti/backoff/v4"
|
"github.com/cenkalti/backoff/v4"
|
||||||
|
|
||||||
"github.com/pterodactyl/wings/system"
|
"github.com/pterodactyl/wings/system"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -118,7 +119,7 @@ func (c *client) requestOnce(ctx context.Context, method, path string, body io.R
|
||||||
return &Response{res}, err
|
return &Response{res}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// request executes a HTTP request against the Panel API. If there is an error
|
// request executes an HTTP request against the Panel API. If there is an error
|
||||||
// encountered with the request it will be retried using an exponential backoff.
|
// encountered with the request it will be retried using an exponential backoff.
|
||||||
// If the error returned from the Panel is due to API throttling or because there
|
// If the error returned from the Panel is due to API throttling or because there
|
||||||
// are invalid authentication credentials provided the request will _not_ be
|
// are invalid authentication credentials provided the request will _not_ be
|
||||||
|
@ -170,7 +171,7 @@ func (c *client) request(ctx context.Context, method, path string, body io.Reade
|
||||||
// This allows for issues with DNS resolution, or rare race conditions due to
|
// This allows for issues with DNS resolution, or rare race conditions due to
|
||||||
// slower SQL queries on the Panel to potentially self-resolve without just
|
// slower SQL queries on the Panel to potentially self-resolve without just
|
||||||
// immediately failing the first request. The example below shows the amount of
|
// immediately failing the first request. The example below shows the amount of
|
||||||
// time that has ellapsed between each call to the handler when an error is
|
// time that has elapsed between each call to the handler when an error is
|
||||||
// returned. You can tweak these values as needed to get the effect you desire.
|
// returned. You can tweak these values as needed to get the effect you desire.
|
||||||
//
|
//
|
||||||
// If maxAttempts is a value greater than 0 the backoff will be capped at a total
|
// If maxAttempts is a value greater than 0 the backoff will be capped at a total
|
||||||
|
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/apex/log"
|
"github.com/apex/log"
|
||||||
|
|
||||||
"github.com/pterodactyl/wings/parser"
|
"github.com/pterodactyl/wings/parser"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -32,7 +33,7 @@ type Pagination struct {
|
||||||
|
|
||||||
// ServerConfigurationResponse holds the server configuration data returned from
|
// ServerConfigurationResponse holds the server configuration data returned from
|
||||||
// the Panel. When a server process is started, Wings communicates with the
|
// 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
|
// Panel to fetch the latest build information as well as get all the details
|
||||||
// needed to parse the given Egg.
|
// needed to parse the given Egg.
|
||||||
//
|
//
|
||||||
// This means we do not need to hit Wings each time part of the server is
|
// This means we do not need to hit Wings each time part of the server is
|
||||||
|
|
|
@ -2,11 +2,8 @@ package downloader
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"emperror.dev/errors"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/google/uuid"
|
|
||||||
"github.com/pterodactyl/wings/server"
|
|
||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
@ -17,11 +14,16 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"emperror.dev/errors"
|
||||||
|
"github.com/google/uuid"
|
||||||
|
|
||||||
|
"github.com/pterodactyl/wings/server"
|
||||||
)
|
)
|
||||||
|
|
||||||
var client = &http.Client{
|
var client = &http.Client{
|
||||||
Timeout: time.Hour * 12,
|
Timeout: time.Hour * 12,
|
||||||
// Disallow any redirect on a HTTP call. This is a security requirement: do not modify
|
// Disallow any redirect on an HTTP call. This is a security requirement: do not modify
|
||||||
// this logic without first ensuring that the new target location IS NOT within the current
|
// this logic without first ensuring that the new target location IS NOT within the current
|
||||||
// instance's local network.
|
// instance's local network.
|
||||||
//
|
//
|
||||||
|
@ -36,9 +38,9 @@ var client = &http.Client{
|
||||||
}
|
}
|
||||||
|
|
||||||
var instance = &Downloader{
|
var instance = &Downloader{
|
||||||
// Tracks all of the active downloads.
|
// Tracks all the active downloads.
|
||||||
downloadCache: make(map[string]*Download),
|
downloadCache: make(map[string]*Download),
|
||||||
// Tracks all of the downloads active for a given server instance. This is
|
// Tracks all the downloads active for a given server instance. This is
|
||||||
// primarily used to make things quicker and keep the code a little more
|
// primarily used to make things quicker and keep the code a little more
|
||||||
// legible throughout here.
|
// legible throughout here.
|
||||||
serverCache: make(map[string][]string),
|
serverCache: make(map[string][]string),
|
||||||
|
@ -196,7 +198,7 @@ func (dl *Download) Cancel() {
|
||||||
|
|
||||||
// Checks if the given download belongs to the provided server.
|
// Checks if the given download belongs to the provided server.
|
||||||
func (dl *Download) BelongsTo(s *server.Server) bool {
|
func (dl *Download) BelongsTo(s *server.Server) bool {
|
||||||
return dl.server.Id() == s.Id()
|
return dl.server.ID() == s.ID()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns the current progress of the download as a float value between 0 and 1 where
|
// Returns the current progress of the download as a float value between 0 and 1 where
|
||||||
|
@ -277,7 +279,7 @@ type Downloader struct {
|
||||||
func (d *Downloader) track(dl *Download) {
|
func (d *Downloader) track(dl *Download) {
|
||||||
d.mu.Lock()
|
d.mu.Lock()
|
||||||
defer d.mu.Unlock()
|
defer d.mu.Unlock()
|
||||||
sid := dl.server.Id()
|
sid := dl.server.ID()
|
||||||
if _, ok := d.downloadCache[dl.Identifier]; !ok {
|
if _, ok := d.downloadCache[dl.Identifier]; !ok {
|
||||||
d.downloadCache[dl.Identifier] = dl
|
d.downloadCache[dl.Identifier] = dl
|
||||||
if _, ok := d.serverCache[sid]; !ok {
|
if _, ok := d.serverCache[sid]; !ok {
|
||||||
|
@ -299,22 +301,22 @@ func (d *Downloader) find(dlid string) *Download {
|
||||||
|
|
||||||
// Remove the given download reference from the cache storing them. This also updates
|
// Remove the given download reference from the cache storing them. This also updates
|
||||||
// the slice of active downloads for a given server to not include this download.
|
// the slice of active downloads for a given server to not include this download.
|
||||||
func (d *Downloader) remove(dlid string) {
|
func (d *Downloader) remove(dlID string) {
|
||||||
d.mu.Lock()
|
d.mu.Lock()
|
||||||
defer d.mu.Unlock()
|
defer d.mu.Unlock()
|
||||||
if _, ok := d.downloadCache[dlid]; !ok {
|
if _, ok := d.downloadCache[dlID]; !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
sid := d.downloadCache[dlid].server.Id()
|
sID := d.downloadCache[dlID].server.ID()
|
||||||
delete(d.downloadCache, dlid)
|
delete(d.downloadCache, dlID)
|
||||||
if tracked, ok := d.serverCache[sid]; ok {
|
if tracked, ok := d.serverCache[sID]; ok {
|
||||||
var out []string
|
var out []string
|
||||||
for _, k := range tracked {
|
for _, k := range tracked {
|
||||||
if k != dlid {
|
if k != dlID {
|
||||||
out = append(out, k)
|
out = append(out, k)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
d.serverCache[sid] = out
|
d.serverCache[sID] = out
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,7 @@ import (
|
||||||
"github.com/apex/log"
|
"github.com/apex/log"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
|
|
||||||
"github.com/pterodactyl/wings/server"
|
"github.com/pterodactyl/wings/server"
|
||||||
"github.com/pterodactyl/wings/server/filesystem"
|
"github.com/pterodactyl/wings/server/filesystem"
|
||||||
)
|
)
|
||||||
|
|
|
@ -2,6 +2,7 @@ package router
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
|
||||||
"github.com/pterodactyl/wings/router/middleware"
|
"github.com/pterodactyl/wings/router/middleware"
|
||||||
"github.com/pterodactyl/wings/server"
|
"github.com/pterodactyl/wings/server"
|
||||||
)
|
)
|
||||||
|
|
|
@ -12,6 +12,7 @@ import (
|
||||||
"github.com/apex/log"
|
"github.com/apex/log"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
|
|
||||||
"github.com/pterodactyl/wings/config"
|
"github.com/pterodactyl/wings/config"
|
||||||
"github.com/pterodactyl/wings/remote"
|
"github.com/pterodactyl/wings/remote"
|
||||||
"github.com/pterodactyl/wings/server"
|
"github.com/pterodactyl/wings/server"
|
||||||
|
@ -62,7 +63,7 @@ func (re *RequestError) Abort(c *gin.Context, status int) {
|
||||||
// server triggered this error.
|
// server triggered this error.
|
||||||
if s, ok := c.Get("server"); ok {
|
if s, ok := c.Get("server"); ok {
|
||||||
if s, ok := s.(*server.Server); ok {
|
if s, ok := s.(*server.Server); ok {
|
||||||
event = event.WithField("server_id", s.Id())
|
event = event.WithField("server_id", s.ID())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -262,14 +263,14 @@ func ServerExists() gin.HandlerFunc {
|
||||||
if c.Param("server") != "" {
|
if c.Param("server") != "" {
|
||||||
manager := ExtractManager(c)
|
manager := ExtractManager(c)
|
||||||
s = manager.Find(func(s *server.Server) bool {
|
s = manager.Find(func(s *server.Server) bool {
|
||||||
return c.Param("server") == s.Id()
|
return c.Param("server") == s.ID()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
if s == nil {
|
if s == nil {
|
||||||
c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"error": "The requested resource does not exist on this instance."})
|
c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"error": "The requested resource does not exist on this instance."})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
c.Set("logger", ExtractLogger(c).WithField("server_id", s.Id()))
|
c.Set("logger", ExtractLogger(c).WithField("server_id", s.ID()))
|
||||||
c.Set("server", s)
|
c.Set("server", s)
|
||||||
c.Next()
|
c.Next()
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ package router
|
||||||
import (
|
import (
|
||||||
"github.com/apex/log"
|
"github.com/apex/log"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
|
||||||
"github.com/pterodactyl/wings/remote"
|
"github.com/pterodactyl/wings/remote"
|
||||||
"github.com/pterodactyl/wings/router/middleware"
|
"github.com/pterodactyl/wings/router/middleware"
|
||||||
"github.com/pterodactyl/wings/server"
|
"github.com/pterodactyl/wings/server"
|
||||||
|
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
|
||||||
"github.com/pterodactyl/wings/router/middleware"
|
"github.com/pterodactyl/wings/router/middleware"
|
||||||
"github.com/pterodactyl/wings/router/tokens"
|
"github.com/pterodactyl/wings/router/tokens"
|
||||||
"github.com/pterodactyl/wings/server/backup"
|
"github.com/pterodactyl/wings/server/backup"
|
||||||
|
|
|
@ -10,6 +10,7 @@ import (
|
||||||
"emperror.dev/errors"
|
"emperror.dev/errors"
|
||||||
"github.com/apex/log"
|
"github.com/apex/log"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
|
||||||
"github.com/pterodactyl/wings/router/downloader"
|
"github.com/pterodactyl/wings/router/downloader"
|
||||||
"github.com/pterodactyl/wings/router/middleware"
|
"github.com/pterodactyl/wings/router/middleware"
|
||||||
"github.com/pterodactyl/wings/router/tokens"
|
"github.com/pterodactyl/wings/router/tokens"
|
||||||
|
@ -195,7 +196,7 @@ func deleteServer(c *gin.Context) {
|
||||||
s.Websockets().CancelAll()
|
s.Websockets().CancelAll()
|
||||||
|
|
||||||
// Remove any pending remote file downloads for the server.
|
// Remove any pending remote file downloads for the server.
|
||||||
for _, dl := range downloader.ByServer(s.Id()) {
|
for _, dl := range downloader.ByServer(s.ID()) {
|
||||||
dl.Cancel()
|
dl.Cancel()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -220,7 +221,7 @@ func deleteServer(c *gin.Context) {
|
||||||
}(s.Filesystem().Path())
|
}(s.Filesystem().Path())
|
||||||
|
|
||||||
middleware.ExtractManager(c).Remove(func(server *server.Server) bool {
|
middleware.ExtractManager(c).Remove(func(server *server.Server) bool {
|
||||||
return server.Id() == s.Id()
|
return server.ID() == s.ID()
|
||||||
})
|
})
|
||||||
|
|
||||||
// Deallocate the reference to this server.
|
// Deallocate the reference to this server.
|
||||||
|
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"emperror.dev/errors"
|
"emperror.dev/errors"
|
||||||
"github.com/apex/log"
|
"github.com/apex/log"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
|
||||||
"github.com/pterodactyl/wings/router/middleware"
|
"github.com/pterodactyl/wings/router/middleware"
|
||||||
"github.com/pterodactyl/wings/server"
|
"github.com/pterodactyl/wings/server"
|
||||||
"github.com/pterodactyl/wings/server/backup"
|
"github.com/pterodactyl/wings/server/backup"
|
||||||
|
@ -42,7 +43,7 @@ func postServerBackup(c *gin.Context) {
|
||||||
// Attach the server ID and the request ID to the adapter log context for easier
|
// Attach the server ID and the request ID to the adapter log context for easier
|
||||||
// parsing in the logs.
|
// parsing in the logs.
|
||||||
adapter.WithLogContext(map[string]interface{}{
|
adapter.WithLogContext(map[string]interface{}{
|
||||||
"server": s.Id(),
|
"server": s.ID(),
|
||||||
"request_id": c.GetString("request_id"),
|
"request_id": c.GetString("request_id"),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -16,12 +16,13 @@ import (
|
||||||
"emperror.dev/errors"
|
"emperror.dev/errors"
|
||||||
"github.com/apex/log"
|
"github.com/apex/log"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
"golang.org/x/sync/errgroup"
|
||||||
|
|
||||||
"github.com/pterodactyl/wings/router/downloader"
|
"github.com/pterodactyl/wings/router/downloader"
|
||||||
"github.com/pterodactyl/wings/router/middleware"
|
"github.com/pterodactyl/wings/router/middleware"
|
||||||
"github.com/pterodactyl/wings/router/tokens"
|
"github.com/pterodactyl/wings/router/tokens"
|
||||||
"github.com/pterodactyl/wings/server"
|
"github.com/pterodactyl/wings/server"
|
||||||
"github.com/pterodactyl/wings/server/filesystem"
|
"github.com/pterodactyl/wings/server/filesystem"
|
||||||
"golang.org/x/sync/errgroup"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// getServerFileContents returns the contents of a file on the server.
|
// getServerFileContents returns the contents of a file on the server.
|
||||||
|
@ -245,7 +246,7 @@ func postServerWriteFile(c *gin.Context) {
|
||||||
func getServerPullingFiles(c *gin.Context) {
|
func getServerPullingFiles(c *gin.Context) {
|
||||||
s := ExtractServer(c)
|
s := ExtractServer(c)
|
||||||
c.JSON(http.StatusOK, gin.H{
|
c.JSON(http.StatusOK, gin.H{
|
||||||
"downloads": downloader.ByServer(s.Id()),
|
"downloads": downloader.ByServer(s.ID()),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -277,7 +278,7 @@ func postServerPullRemoteFile(c *gin.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// Do not allow more than three simultaneous remote file downloads at one time.
|
// Do not allow more than three simultaneous remote file downloads at one time.
|
||||||
if len(downloader.ByServer(s.Id())) >= 3 {
|
if len(downloader.ByServer(s.ID())) >= 3 {
|
||||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{
|
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{
|
||||||
"error": "This server has reached its limit of 3 simultaneous remote file downloads at once. Please wait for one to complete before trying again.",
|
"error": "This server has reached its limit of 3 simultaneous remote file downloads at once. Please wait for one to complete before trying again.",
|
||||||
})
|
})
|
||||||
|
|
|
@ -7,6 +7,7 @@ import (
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
ws "github.com/gorilla/websocket"
|
ws "github.com/gorilla/websocket"
|
||||||
|
|
||||||
"github.com/pterodactyl/wings/router/middleware"
|
"github.com/pterodactyl/wings/router/middleware"
|
||||||
"github.com/pterodactyl/wings/router/websocket"
|
"github.com/pterodactyl/wings/router/websocket"
|
||||||
)
|
)
|
||||||
|
|
|
@ -9,6 +9,7 @@ import (
|
||||||
|
|
||||||
"github.com/apex/log"
|
"github.com/apex/log"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
|
||||||
"github.com/pterodactyl/wings/config"
|
"github.com/pterodactyl/wings/config"
|
||||||
"github.com/pterodactyl/wings/installer"
|
"github.com/pterodactyl/wings/installer"
|
||||||
"github.com/pterodactyl/wings/router/middleware"
|
"github.com/pterodactyl/wings/router/middleware"
|
||||||
|
@ -78,18 +79,18 @@ func postCreateServer(c *gin.Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if i.Server().Config().StartOnCompletion {
|
if i.Server().Config().StartOnCompletion {
|
||||||
log.WithField("server_id", i.Server().Id()).Debug("starting server after successful installation")
|
log.WithField("server_id", i.Server().ID()).Debug("starting server after successful installation")
|
||||||
if err := i.Server().HandlePowerAction(server.PowerActionStart, 30); err != nil {
|
if err := i.Server().HandlePowerAction(server.PowerActionStart, 30); err != nil {
|
||||||
if errors.Is(err, context.DeadlineExceeded) {
|
if errors.Is(err, context.DeadlineExceeded) {
|
||||||
log.WithFields(log.Fields{"server_id": i.Server().Id(), "action": "start"}).
|
log.WithFields(log.Fields{"server_id": i.Server().ID(), "action": "start"}).
|
||||||
Warn("could not acquire a lock while attempting to perform a power action")
|
Warn("could not acquire a lock while attempting to perform a power action")
|
||||||
} else {
|
} else {
|
||||||
log.WithFields(log.Fields{"server_id": i.Server().Id(), "action": "start", "error": err}).
|
log.WithFields(log.Fields{"server_id": i.Server().ID(), "action": "start", "error": err}).
|
||||||
Error("encountered error processing a server power action in the background")
|
Error("encountered error processing a server power action in the background")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
log.WithField("server_id", i.Server().Id()).
|
log.WithField("server_id", i.Server().ID()).
|
||||||
Debug("skipping automatic start after successful server installation")
|
Debug("skipping automatic start after successful server installation")
|
||||||
}
|
}
|
||||||
}(install)
|
}(install)
|
||||||
|
|
|
@ -23,6 +23,7 @@ import (
|
||||||
"github.com/juju/ratelimit"
|
"github.com/juju/ratelimit"
|
||||||
"github.com/mholt/archiver/v3"
|
"github.com/mholt/archiver/v3"
|
||||||
"github.com/mitchellh/colorstring"
|
"github.com/mitchellh/colorstring"
|
||||||
|
|
||||||
"github.com/pterodactyl/wings/config"
|
"github.com/pterodactyl/wings/config"
|
||||||
"github.com/pterodactyl/wings/installer"
|
"github.com/pterodactyl/wings/installer"
|
||||||
"github.com/pterodactyl/wings/remote"
|
"github.com/pterodactyl/wings/remote"
|
||||||
|
@ -75,14 +76,14 @@ func getServerArchive(c *gin.Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
s := ExtractServer(c)
|
s := ExtractServer(c)
|
||||||
if token.Subject != s.Id() {
|
if token.Subject != s.ID() {
|
||||||
c.AbortWithStatusJSON(http.StatusForbidden, gin.H{
|
c.AbortWithStatusJSON(http.StatusForbidden, gin.H{
|
||||||
"error": "Missing required token subject, or subject is not valid for the requested server.",
|
"error": "Missing required token subject, or subject is not valid for the requested server.",
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
archivePath := getArchivePath(s.Id())
|
archivePath := getArchivePath(s.ID())
|
||||||
|
|
||||||
// Stat the archive file.
|
// Stat the archive file.
|
||||||
st, err := os.Lstat(archivePath)
|
st, err := os.Lstat(archivePath)
|
||||||
|
@ -123,7 +124,7 @@ func getServerArchive(c *gin.Context) {
|
||||||
c.Header("X-Checksum", checksum)
|
c.Header("X-Checksum", checksum)
|
||||||
c.Header("X-Mime-Type", "application/tar+gzip")
|
c.Header("X-Mime-Type", "application/tar+gzip")
|
||||||
c.Header("Content-Length", strconv.Itoa(int(st.Size())))
|
c.Header("Content-Length", strconv.Itoa(int(st.Size())))
|
||||||
c.Header("Content-Disposition", "attachment; filename="+strconv.Quote(s.Id()+".tar.gz"))
|
c.Header("Content-Disposition", "attachment; filename="+strconv.Quote(s.ID()+".tar.gz"))
|
||||||
c.Header("Content-Type", "application/octet-stream")
|
c.Header("Content-Type", "application/octet-stream")
|
||||||
|
|
||||||
_, _ = bufio.NewReader(f).WriteTo(c.Writer)
|
_, _ = bufio.NewReader(f).WriteTo(c.Writer)
|
||||||
|
@ -134,7 +135,7 @@ func postServerArchive(c *gin.Context) {
|
||||||
manager := middleware.ExtractManager(c)
|
manager := middleware.ExtractManager(c)
|
||||||
|
|
||||||
go func(s *server.Server) {
|
go func(s *server.Server) {
|
||||||
l := log.WithField("server", s.Id())
|
l := log.WithField("server", s.ID())
|
||||||
|
|
||||||
// This function automatically adds the Source Node prefix and Timestamp to the log
|
// This function automatically adds the Source Node prefix and Timestamp to the log
|
||||||
// output before sending it over the websocket.
|
// output before sending it over the websocket.
|
||||||
|
@ -157,7 +158,7 @@ func postServerArchive(c *gin.Context) {
|
||||||
s.Events().Publish(server.TransferStatusEvent, "failure")
|
s.Events().Publish(server.TransferStatusEvent, "failure")
|
||||||
|
|
||||||
sendTransferLog("Attempting to notify panel of archive failure..")
|
sendTransferLog("Attempting to notify panel of archive failure..")
|
||||||
if err := manager.Client().SetArchiveStatus(s.Context(), s.Id(), false); err != nil {
|
if err := manager.Client().SetArchiveStatus(s.Context(), s.ID(), false); err != nil {
|
||||||
if !remote.IsRequestError(err) {
|
if !remote.IsRequestError(err) {
|
||||||
sendTransferLog("Failed to notify panel of archive failure: " + err.Error())
|
sendTransferLog("Failed to notify panel of archive failure: " + err.Error())
|
||||||
l.WithField("error", err).Error("failed to notify panel of failed archive status")
|
l.WithField("error", err).Error("failed to notify panel of failed archive status")
|
||||||
|
@ -190,7 +191,7 @@ func postServerArchive(c *gin.Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Attempt to get an archive of the server.
|
// Attempt to get an archive of the server.
|
||||||
if err := a.Create(getArchivePath(s.Id())); err != nil {
|
if err := a.Create(getArchivePath(s.ID())); err != nil {
|
||||||
sendTransferLog("An error occurred while archiving the server: " + err.Error())
|
sendTransferLog("An error occurred while archiving the server: " + err.Error())
|
||||||
l.WithField("error", err).Error("failed to get transfer archive for server")
|
l.WithField("error", err).Error("failed to get transfer archive for server")
|
||||||
return
|
return
|
||||||
|
@ -199,7 +200,7 @@ func postServerArchive(c *gin.Context) {
|
||||||
sendTransferLog("Successfully created archive, attempting to notify panel..")
|
sendTransferLog("Successfully created archive, attempting to notify panel..")
|
||||||
l.Info("successfully created server transfer archive, notifying panel..")
|
l.Info("successfully created server transfer archive, notifying panel..")
|
||||||
|
|
||||||
if err := manager.Client().SetArchiveStatus(s.Context(), s.Id(), true); err != nil {
|
if err := manager.Client().SetArchiveStatus(s.Context(), s.ID(), true); err != nil {
|
||||||
if !remote.IsRequestError(err) {
|
if !remote.IsRequestError(err) {
|
||||||
sendTransferLog("Failed to notify panel of archive success: " + err.Error())
|
sendTransferLog("Failed to notify panel of archive success: " + err.Error())
|
||||||
l.WithField("error", err).Error("failed to notify panel of successful archive status")
|
l.WithField("error", err).Error("failed to notify panel of successful archive status")
|
||||||
|
@ -360,7 +361,7 @@ func postTransfer(c *gin.Context) {
|
||||||
sendTransferLog("Server transfer failed, check Wings logs for additional information.")
|
sendTransferLog("Server transfer failed, check Wings logs for additional information.")
|
||||||
s.Events().Publish(server.TransferStatusEvent, "failure")
|
s.Events().Publish(server.TransferStatusEvent, "failure")
|
||||||
manager.Remove(func(match *server.Server) bool {
|
manager.Remove(func(match *server.Server) bool {
|
||||||
return match.Id() == s.Id()
|
return match.ID() == s.ID()
|
||||||
})
|
})
|
||||||
|
|
||||||
// If the transfer status was successful but the request failed, act like the transfer failed.
|
// If the transfer status was successful but the request failed, act like the transfer failed.
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
package tokens
|
package tokens
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/gbrlsnchs/jwt/v3"
|
|
||||||
"github.com/pterodactyl/wings/config"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/gbrlsnchs/jwt/v3"
|
||||||
|
|
||||||
|
"github.com/pterodactyl/wings/config"
|
||||||
)
|
)
|
||||||
|
|
||||||
type TokenData interface {
|
type TokenData interface {
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
package tokens
|
package tokens
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/patrickmn/go-cache"
|
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/patrickmn/go-cache"
|
||||||
)
|
)
|
||||||
|
|
||||||
type TokenStore struct {
|
type TokenStore struct {
|
||||||
|
|
|
@ -2,11 +2,12 @@ package tokens
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"github.com/apex/log"
|
|
||||||
"github.com/gbrlsnchs/jwt/v3"
|
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/apex/log"
|
||||||
|
"github.com/gbrlsnchs/jwt/v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
// The time at which Wings was booted. No JWT's created before this time are allowed to
|
// The time at which Wings was booted. No JWT's created before this time are allowed to
|
||||||
|
|
|
@ -2,9 +2,10 @@ package websocket
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/pterodactyl/wings/events"
|
"github.com/pterodactyl/wings/events"
|
||||||
"github.com/pterodactyl/wings/server"
|
"github.com/pterodactyl/wings/server"
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Checks the time to expiration on the JWT every 30 seconds until the token has
|
// Checks the time to expiration on the JWT every 30 seconds until the token has
|
||||||
|
|
|
@ -2,22 +2,24 @@ package websocket
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"emperror.dev/errors"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"emperror.dev/errors"
|
||||||
"github.com/apex/log"
|
"github.com/apex/log"
|
||||||
"github.com/gbrlsnchs/jwt/v3"
|
"github.com/gbrlsnchs/jwt/v3"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"github.com/gorilla/websocket"
|
"github.com/gorilla/websocket"
|
||||||
|
|
||||||
"github.com/pterodactyl/wings/config"
|
"github.com/pterodactyl/wings/config"
|
||||||
"github.com/pterodactyl/wings/environment"
|
"github.com/pterodactyl/wings/environment"
|
||||||
"github.com/pterodactyl/wings/environment/docker"
|
"github.com/pterodactyl/wings/environment/docker"
|
||||||
"github.com/pterodactyl/wings/router/tokens"
|
"github.com/pterodactyl/wings/router/tokens"
|
||||||
"github.com/pterodactyl/wings/server"
|
"github.com/pterodactyl/wings/server"
|
||||||
"net/http"
|
|
||||||
"strings"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -55,11 +57,10 @@ func IsJwtError(err error) bool {
|
||||||
errors.Is(err, jwt.ErrExpValidation)
|
errors.Is(err, jwt.ErrExpValidation)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parses a JWT into a websocket token payload.
|
// NewTokenPayload parses a JWT into a websocket token payload.
|
||||||
func NewTokenPayload(token []byte) (*tokens.WebsocketPayload, error) {
|
func NewTokenPayload(token []byte) (*tokens.WebsocketPayload, error) {
|
||||||
payload := tokens.WebsocketPayload{}
|
var payload tokens.WebsocketPayload
|
||||||
err := tokens.ParseToken(token, &payload)
|
if err := tokens.ParseToken(token, &payload); err != nil {
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -180,7 +181,7 @@ func (h *Handler) unsafeSendJson(v interface{}) error {
|
||||||
return h.Connection.WriteJSON(v)
|
return h.Connection.WriteJSON(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Checks if the JWT is still valid.
|
// TokenValid checks if the JWT is still valid.
|
||||||
func (h *Handler) TokenValid() error {
|
func (h *Handler) TokenValid() error {
|
||||||
j := h.GetJwt()
|
j := h.GetJwt()
|
||||||
if j == nil {
|
if j == nil {
|
||||||
|
@ -199,14 +200,14 @@ func (h *Handler) TokenValid() error {
|
||||||
return ErrJwtNoConnectPerm
|
return ErrJwtNoConnectPerm
|
||||||
}
|
}
|
||||||
|
|
||||||
if h.server.Id() != j.GetServerUuid() {
|
if h.server.ID() != j.GetServerUuid() {
|
||||||
return ErrJwtUuidMismatch
|
return ErrJwtUuidMismatch
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sends an error back to the connected websocket instance by checking the permissions
|
// SendErrorJson sends an error back to the connected websocket instance by checking the permissions
|
||||||
// of the token. If the user has the "receive-errors" grant we will send back the actual
|
// of the token. If the user has the "receive-errors" grant we will send back the actual
|
||||||
// error message, otherwise we just send back a standard error message.
|
// error message, otherwise we just send back a standard error message.
|
||||||
func (h *Handler) SendErrorJson(msg Message, err error, shouldLog ...bool) error {
|
func (h *Handler) SendErrorJson(msg Message, err error, shouldLog ...bool) error {
|
||||||
|
@ -236,7 +237,7 @@ func (h *Handler) SendErrorJson(msg Message, err error, shouldLog ...bool) error
|
||||||
return h.unsafeSendJson(wsm)
|
return h.unsafeSendJson(wsm)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Converts an error message into a more readable representation and returns a UUID
|
// GetErrorMessage converts an error message into a more readable representation and returns a UUID
|
||||||
// that can be cross-referenced to find the specific error that triggered.
|
// that can be cross-referenced to find the specific error that triggered.
|
||||||
func (h *Handler) GetErrorMessage(msg string) (string, uuid.UUID) {
|
func (h *Handler) GetErrorMessage(msg string) (string, uuid.UUID) {
|
||||||
u := uuid.Must(uuid.NewRandom())
|
u := uuid.Must(uuid.NewRandom())
|
||||||
|
@ -246,13 +247,7 @@ func (h *Handler) GetErrorMessage(msg string) (string, uuid.UUID) {
|
||||||
return m, u
|
return m, u
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sets the JWT for the websocket in a race-safe manner.
|
// GetJwt returns the JWT for the websocket in a race-safe manner.
|
||||||
func (h *Handler) setJwt(token *tokens.WebsocketPayload) {
|
|
||||||
h.Lock()
|
|
||||||
h.jwt = token
|
|
||||||
h.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *Handler) GetJwt() *tokens.WebsocketPayload {
|
func (h *Handler) GetJwt() *tokens.WebsocketPayload {
|
||||||
h.RLock()
|
h.RLock()
|
||||||
defer h.RUnlock()
|
defer h.RUnlock()
|
||||||
|
@ -260,7 +255,14 @@ func (h *Handler) GetJwt() *tokens.WebsocketPayload {
|
||||||
return h.jwt
|
return h.jwt
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle the inbound socket request and route it to the proper server action.
|
// setJwt sets the JWT for the websocket in a race-safe manner.
|
||||||
|
func (h *Handler) setJwt(token *tokens.WebsocketPayload) {
|
||||||
|
h.Lock()
|
||||||
|
h.jwt = token
|
||||||
|
h.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
// HandleInbound handles an inbound socket request and route it to the proper action.
|
||||||
func (h *Handler) HandleInbound(m Message) error {
|
func (h *Handler) HandleInbound(m Message) error {
|
||||||
if m.Event != AuthenticationEvent {
|
if m.Event != AuthenticationEvent {
|
||||||
if err := h.TokenValid(); err != nil {
|
if err := h.TokenValid(); err != nil {
|
||||||
|
|
|
@ -62,7 +62,7 @@ func (s *Server) Backup(b backup.BackupInterface) error {
|
||||||
ignored := b.Ignored()
|
ignored := b.Ignored()
|
||||||
if b.Ignored() == "" {
|
if b.Ignored() == "" {
|
||||||
if i, err := s.getServerwideIgnoredFiles(); err != nil {
|
if i, err := s.getServerwideIgnoredFiles(); err != nil {
|
||||||
log.WithField("server", s.Id()).WithField("error", err).Warn("failed to get server-wide ignored files")
|
log.WithField("server", s.ID()).WithField("error", err).Warn("failed to get server-wide ignored files")
|
||||||
} else {
|
} else {
|
||||||
ignored = i
|
ignored = i
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,12 +6,10 @@ import (
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"emperror.dev/errors"
|
"emperror.dev/errors"
|
||||||
|
|
||||||
"github.com/pterodactyl/wings/server/filesystem"
|
|
||||||
|
|
||||||
"github.com/mholt/archiver/v3"
|
"github.com/mholt/archiver/v3"
|
||||||
|
|
||||||
"github.com/pterodactyl/wings/remote"
|
"github.com/pterodactyl/wings/remote"
|
||||||
|
"github.com/pterodactyl/wings/server/filesystem"
|
||||||
)
|
)
|
||||||
|
|
||||||
type LocalBackup struct {
|
type LocalBackup struct {
|
||||||
|
|
|
@ -9,6 +9,7 @@ import (
|
||||||
|
|
||||||
"emperror.dev/errors"
|
"emperror.dev/errors"
|
||||||
"github.com/mitchellh/colorstring"
|
"github.com/mitchellh/colorstring"
|
||||||
|
|
||||||
"github.com/pterodactyl/wings/config"
|
"github.com/pterodactyl/wings/config"
|
||||||
"github.com/pterodactyl/wings/system"
|
"github.com/pterodactyl/wings/system"
|
||||||
)
|
)
|
||||||
|
|
|
@ -14,7 +14,7 @@ import (
|
||||||
"github.com/juju/ratelimit"
|
"github.com/juju/ratelimit"
|
||||||
"github.com/karrick/godirwalk"
|
"github.com/karrick/godirwalk"
|
||||||
"github.com/klauspost/pgzip"
|
"github.com/klauspost/pgzip"
|
||||||
"github.com/sabhiram/go-gitignore"
|
ignore "github.com/sabhiram/go-gitignore"
|
||||||
|
|
||||||
"github.com/pterodactyl/wings/config"
|
"github.com/pterodactyl/wings/config"
|
||||||
)
|
)
|
||||||
|
|
|
@ -17,9 +17,10 @@ import (
|
||||||
"emperror.dev/errors"
|
"emperror.dev/errors"
|
||||||
"github.com/gabriel-vasile/mimetype"
|
"github.com/gabriel-vasile/mimetype"
|
||||||
"github.com/karrick/godirwalk"
|
"github.com/karrick/godirwalk"
|
||||||
|
ignore "github.com/sabhiram/go-gitignore"
|
||||||
|
|
||||||
"github.com/pterodactyl/wings/config"
|
"github.com/pterodactyl/wings/config"
|
||||||
"github.com/pterodactyl/wings/system"
|
"github.com/pterodactyl/wings/system"
|
||||||
ignore "github.com/sabhiram/go-gitignore"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Filesystem struct {
|
type Filesystem struct {
|
||||||
|
|
|
@ -12,6 +12,7 @@ import (
|
||||||
"unicode/utf8"
|
"unicode/utf8"
|
||||||
|
|
||||||
. "github.com/franela/goblin"
|
. "github.com/franela/goblin"
|
||||||
|
|
||||||
"github.com/pterodactyl/wings/config"
|
"github.com/pterodactyl/wings/config"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,7 @@ import (
|
||||||
"github.com/docker/docker/api/types/container"
|
"github.com/docker/docker/api/types/container"
|
||||||
"github.com/docker/docker/api/types/mount"
|
"github.com/docker/docker/api/types/mount"
|
||||||
"github.com/docker/docker/client"
|
"github.com/docker/docker/client"
|
||||||
|
|
||||||
"github.com/pterodactyl/wings/config"
|
"github.com/pterodactyl/wings/config"
|
||||||
"github.com/pterodactyl/wings/environment"
|
"github.com/pterodactyl/wings/environment"
|
||||||
"github.com/pterodactyl/wings/remote"
|
"github.com/pterodactyl/wings/remote"
|
||||||
|
@ -88,7 +89,7 @@ func (s *Server) Reinstall() error {
|
||||||
|
|
||||||
// Internal installation function used to simplify reporting back to the Panel.
|
// Internal installation function used to simplify reporting back to the Panel.
|
||||||
func (s *Server) internalInstall() error {
|
func (s *Server) internalInstall() error {
|
||||||
script, err := s.client.GetInstallationScript(s.Context(), s.Id())
|
script, err := s.client.GetInstallationScript(s.Context(), s.ID())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -156,7 +157,7 @@ func (s *Server) SetRestoring(state bool) {
|
||||||
|
|
||||||
// Removes the installer container for the server.
|
// Removes the installer container for the server.
|
||||||
func (ip *InstallationProcess) RemoveContainer() error {
|
func (ip *InstallationProcess) RemoveContainer() error {
|
||||||
err := ip.client.ContainerRemove(ip.context, ip.Server.Id()+"_installer", types.ContainerRemoveOptions{
|
err := ip.client.ContainerRemove(ip.context, ip.Server.ID()+"_installer", types.ContainerRemoveOptions{
|
||||||
RemoveVolumes: true,
|
RemoveVolumes: true,
|
||||||
Force: true,
|
Force: true,
|
||||||
})
|
})
|
||||||
|
@ -206,7 +207,7 @@ func (ip *InstallationProcess) Run() error {
|
||||||
|
|
||||||
// Returns the location of the temporary data for the installation process.
|
// Returns the location of the temporary data for the installation process.
|
||||||
func (ip *InstallationProcess) tempDir() string {
|
func (ip *InstallationProcess) tempDir() string {
|
||||||
return filepath.Join(os.TempDir(), "pterodactyl/", ip.Server.Id())
|
return filepath.Join(os.TempDir(), "pterodactyl/", ip.Server.ID())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Writes the installation script to a temporary file on the host machine so that it
|
// Writes the installation script to a temporary file on the host machine so that it
|
||||||
|
@ -329,7 +330,7 @@ func (ip *InstallationProcess) BeforeExecute() error {
|
||||||
|
|
||||||
// Returns the log path for the installation process.
|
// Returns the log path for the installation process.
|
||||||
func (ip *InstallationProcess) GetLogPath() string {
|
func (ip *InstallationProcess) GetLogPath() string {
|
||||||
return filepath.Join(config.Get().System.LogDirectory, "/install", ip.Server.Id()+".log")
|
return filepath.Join(config.Get().System.LogDirectory, "/install", ip.Server.ID()+".log")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cleans up after the execution of the installation process. This grabs the logs from the
|
// Cleans up after the execution of the installation process. This grabs the logs from the
|
||||||
|
@ -365,7 +366,7 @@ func (ip *InstallationProcess) AfterExecute(containerId string) error {
|
||||||
|
|
|
|
||||||
| Details
|
| Details
|
||||||
| ------------------------------
|
| ------------------------------
|
||||||
Server UUID: {{.Server.Id}}
|
Server UUID: {{.Server.ID}}
|
||||||
Container Image: {{.Script.ContainerImage}}
|
Container Image: {{.Script.ContainerImage}}
|
||||||
Container Entrypoint: {{.Script.Entrypoint}}
|
Container Entrypoint: {{.Script.Entrypoint}}
|
||||||
|
|
||||||
|
@ -469,7 +470,7 @@ func (ip *InstallationProcess) Execute() (string, error) {
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
r, err := ip.client.ContainerCreate(ctx, conf, hostConf, nil, nil, ip.Server.Id()+"_installer")
|
r, err := ip.client.ContainerCreate(ctx, conf, hostConf, nil, nil, ip.Server.ID()+"_installer")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
@ -573,5 +574,5 @@ func (ip *InstallationProcess) resourceLimits() container.Resources {
|
||||||
// server is. A boolean value of "true" means everything was successful, "false"
|
// server is. A boolean value of "true" means everything was successful, "false"
|
||||||
// means something went wrong and the server must be deleted and re-created.
|
// means something went wrong and the server must be deleted and re-created.
|
||||||
func (s *Server) SyncInstallState(successful bool) error {
|
func (s *Server) SyncInstallState(successful bool) error {
|
||||||
return s.client.SetInstallationStatus(s.Context(), s.Id(), successful)
|
return s.client.SetInstallationStatus(s.Context(), s.ID(), successful)
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ import (
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/apex/log"
|
"github.com/apex/log"
|
||||||
|
|
||||||
"github.com/pterodactyl/wings/config"
|
"github.com/pterodactyl/wings/config"
|
||||||
"github.com/pterodactyl/wings/environment"
|
"github.com/pterodactyl/wings/environment"
|
||||||
"github.com/pterodactyl/wings/events"
|
"github.com/pterodactyl/wings/events"
|
||||||
|
@ -40,7 +41,7 @@ func (dsl *diskSpaceLimiter) Reset() {
|
||||||
// 15 seconds, and terminate it forcefully if it does not stop.
|
// 15 seconds, and terminate it forcefully if it does not stop.
|
||||||
//
|
//
|
||||||
// This function is only executed one time, so whenever a server is marked as booting the limiter
|
// This function is only executed one time, so whenever a server is marked as booting the limiter
|
||||||
// should be reset so it can properly be triggered as needed.
|
// should be reset, so it can properly be triggered as needed.
|
||||||
func (dsl *diskSpaceLimiter) Trigger() {
|
func (dsl *diskSpaceLimiter) Trigger() {
|
||||||
dsl.o.Do(func() {
|
dsl.o.Do(func() {
|
||||||
dsl.server.PublishConsoleOutputFromDaemon("Server is exceeding the assigned disk space limit, stopping process now.")
|
dsl.server.PublishConsoleOutputFromDaemon("Server is exceeding the assigned disk space limit, stopping process now.")
|
||||||
|
@ -50,7 +51,7 @@ func (dsl *diskSpaceLimiter) Trigger() {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Adds all of the internal event listeners we want to use for a server. These listeners can only be
|
// StartEventListeners adds all the internal event listeners we want to use for a server. These listeners can only be
|
||||||
// removed by deleting the server as they should last for the duration of the process' lifetime.
|
// removed by deleting the server as they should last for the duration of the process' lifetime.
|
||||||
func (s *Server) StartEventListeners() {
|
func (s *Server) StartEventListeners() {
|
||||||
console := func(e events.Event) {
|
console := func(e events.Event) {
|
||||||
|
@ -106,15 +107,15 @@ func (s *Server) StartEventListeners() {
|
||||||
}
|
}
|
||||||
|
|
||||||
stats := func(e events.Event) {
|
stats := func(e events.Event) {
|
||||||
st := new(environment.Stats)
|
var st environment.Stats
|
||||||
if err := json.Unmarshal([]byte(e.Data), st); err != nil {
|
if err := json.Unmarshal([]byte(e.Data), &st); err != nil {
|
||||||
s.Log().WithField("error", err).Warn("failed to unmarshal server environment stats")
|
s.Log().WithField("error", err).Warn("failed to unmarshal server environment stats")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the server resource tracking object with the resources we got here.
|
// Update the server resource tracking object with the resources we got here.
|
||||||
s.resources.mu.Lock()
|
s.resources.mu.Lock()
|
||||||
s.resources.Stats = *st
|
s.resources.Stats = st
|
||||||
s.resources.mu.Unlock()
|
s.resources.mu.Unlock()
|
||||||
|
|
||||||
// If there is no disk space available at this point, trigger the server disk limiter logic
|
// If there is no disk space available at this point, trigger the server disk limiter logic
|
||||||
|
|
|
@ -15,6 +15,7 @@ import (
|
||||||
"emperror.dev/errors"
|
"emperror.dev/errors"
|
||||||
"github.com/apex/log"
|
"github.com/apex/log"
|
||||||
"github.com/gammazero/workerpool"
|
"github.com/gammazero/workerpool"
|
||||||
|
|
||||||
"github.com/pterodactyl/wings/config"
|
"github.com/pterodactyl/wings/config"
|
||||||
"github.com/pterodactyl/wings/environment"
|
"github.com/pterodactyl/wings/environment"
|
||||||
"github.com/pterodactyl/wings/environment/docker"
|
"github.com/pterodactyl/wings/environment/docker"
|
||||||
|
@ -28,9 +29,9 @@ type Manager struct {
|
||||||
servers []*Server
|
servers []*Server
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewManager returns a new server manager instance. This will boot up all of
|
// NewManager returns a new server manager instance. This will boot up all the
|
||||||
// the servers that are currently present on the filesystem and set them into
|
// servers that are currently present on the filesystem and set them into the
|
||||||
// the manager.
|
// manager.
|
||||||
func NewManager(ctx context.Context, client remote.Client) (*Manager, error) {
|
func NewManager(ctx context.Context, client remote.Client) (*Manager, error) {
|
||||||
m := NewEmptyManager(client)
|
m := NewEmptyManager(client)
|
||||||
if err := m.init(ctx); err != nil {
|
if err := m.init(ctx); err != nil {
|
||||||
|
@ -52,7 +53,7 @@ func (m *Manager) Client() remote.Client {
|
||||||
return m.client
|
return m.client
|
||||||
}
|
}
|
||||||
|
|
||||||
// Put replaces all of the current values in the collection with the value that
|
// Put replaces all the current values in the collection with the value that
|
||||||
// is passed through.
|
// is passed through.
|
||||||
func (m *Manager) Put(s []*Server) {
|
func (m *Manager) Put(s []*Server) {
|
||||||
m.mu.Lock()
|
m.mu.Lock()
|
||||||
|
@ -60,7 +61,7 @@ func (m *Manager) Put(s []*Server) {
|
||||||
m.mu.Unlock()
|
m.mu.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
// All returns all of the items in the collection.
|
// All returns all the items in the collection.
|
||||||
func (m *Manager) All() []*Server {
|
func (m *Manager) All() []*Server {
|
||||||
m.mu.RLock()
|
m.mu.RLock()
|
||||||
defer m.mu.RUnlock()
|
defer m.mu.RUnlock()
|
||||||
|
@ -78,7 +79,7 @@ func (m *Manager) Add(s *Server) {
|
||||||
// found in the global collection or not.
|
// found in the global collection or not.
|
||||||
func (m *Manager) Get(uuid string) (*Server, bool) {
|
func (m *Manager) Get(uuid string) (*Server, bool) {
|
||||||
match := m.Find(func(server *Server) bool {
|
match := m.Find(func(server *Server) bool {
|
||||||
return server.Id() == uuid
|
return server.ID() == uuid
|
||||||
})
|
})
|
||||||
return match, match != nil
|
return match, match != nil
|
||||||
}
|
}
|
||||||
|
@ -130,7 +131,7 @@ func (m *Manager) Remove(filter func(match *Server) bool) {
|
||||||
func (m *Manager) PersistStates() error {
|
func (m *Manager) PersistStates() error {
|
||||||
states := map[string]string{}
|
states := map[string]string{}
|
||||||
for _, s := range m.All() {
|
for _, s := range m.All() {
|
||||||
states[s.Id()] = s.Environment.State()
|
states[s.ID()] = s.Environment.State()
|
||||||
}
|
}
|
||||||
data, err := json.Marshal(states)
|
data, err := json.Marshal(states)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -175,11 +176,11 @@ func (m *Manager) InitServer(data remote.ServerConfigurationResponse) (*Server,
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
s.fs = filesystem.New(filepath.Join(config.Get().System.Data, s.Id()), s.DiskSpace(), s.Config().Egg.FileDenylist)
|
s.fs = filesystem.New(filepath.Join(config.Get().System.Data, s.ID()), s.DiskSpace(), s.Config().Egg.FileDenylist)
|
||||||
|
|
||||||
// Right now we only support a Docker based environment, so I'm going to hard code
|
// 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
|
// this logic in. When we're ready to support other environment we'll need to make
|
||||||
// some modifications here obviously.
|
// some modifications here, obviously.
|
||||||
settings := environment.Settings{
|
settings := environment.Settings{
|
||||||
Mounts: s.Mounts(),
|
Mounts: s.Mounts(),
|
||||||
Allocations: s.cfg.Allocations,
|
Allocations: s.cfg.Allocations,
|
||||||
|
@ -191,7 +192,7 @@ func (m *Manager) InitServer(data remote.ServerConfigurationResponse) (*Server,
|
||||||
Image: s.Config().Container.Image,
|
Image: s.Config().Container.Image,
|
||||||
}
|
}
|
||||||
|
|
||||||
if env, err := docker.New(s.Id(), &meta, envCfg); err != nil {
|
if env, err := docker.New(s.ID(), &meta, envCfg); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else {
|
} else {
|
||||||
s.Environment = env
|
s.Environment = env
|
||||||
|
@ -212,7 +213,7 @@ func (m *Manager) InitServer(data remote.ServerConfigurationResponse) (*Server,
|
||||||
return s, nil
|
return s, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// initializeFromRemoteSource iterates over a given directory and loads all of
|
// initializeFromRemoteSource iterates over a given directory and loads all
|
||||||
// the servers listed before returning them to the calling function.
|
// the servers listed before returning them to the calling function.
|
||||||
func (m *Manager) init(ctx context.Context) error {
|
func (m *Manager) init(ctx context.Context) error {
|
||||||
log.Info("fetching list of servers from API")
|
log.Info("fetching list of servers from API")
|
||||||
|
@ -252,7 +253,7 @@ func (m *Manager) init(ctx context.Context) error {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wait until we've processed all of the configuration files in the directory
|
// Wait until we've processed all the configuration files in the directory
|
||||||
// before continuing.
|
// before continuing.
|
||||||
pool.StopWait()
|
pool.StopWait()
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/apex/log"
|
"github.com/apex/log"
|
||||||
|
|
||||||
"github.com/pterodactyl/wings/config"
|
"github.com/pterodactyl/wings/config"
|
||||||
"github.com/pterodactyl/wings/environment"
|
"github.com/pterodactyl/wings/environment"
|
||||||
)
|
)
|
||||||
|
|
|
@ -6,9 +6,10 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"emperror.dev/errors"
|
"emperror.dev/errors"
|
||||||
|
"golang.org/x/sync/semaphore"
|
||||||
|
|
||||||
"github.com/pterodactyl/wings/config"
|
"github.com/pterodactyl/wings/config"
|
||||||
"github.com/pterodactyl/wings/environment"
|
"github.com/pterodactyl/wings/environment"
|
||||||
"golang.org/x/sync/semaphore"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type PowerAction string
|
type PowerAction string
|
||||||
|
@ -18,7 +19,7 @@ type PowerAction string
|
||||||
// example, sending two "start" actions back to back will not process the second action until
|
// example, sending two "start" actions back to back will not process the second action until
|
||||||
// the first action has been completed.
|
// the first action has been completed.
|
||||||
//
|
//
|
||||||
// This utilizes a workerpool with a limit of one worker so that all of the actions execute
|
// This utilizes a workerpool with a limit of one worker so that all the actions execute
|
||||||
// in a sync manner.
|
// in a sync manner.
|
||||||
const (
|
const (
|
||||||
PowerActionStart = "start"
|
PowerActionStart = "start"
|
||||||
|
@ -27,7 +28,7 @@ const (
|
||||||
PowerActionTerminate = "kill"
|
PowerActionTerminate = "kill"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Checks if the power action being received is valid.
|
// IsValid checks if the power action being received is valid.
|
||||||
func (pa PowerAction) IsValid() bool {
|
func (pa PowerAction) IsValid() bool {
|
||||||
return pa == PowerActionStart ||
|
return pa == PowerActionStart ||
|
||||||
pa == PowerActionStop ||
|
pa == PowerActionStop ||
|
||||||
|
@ -39,7 +40,7 @@ func (pa PowerAction) IsStart() bool {
|
||||||
return pa == PowerActionStart || pa == PowerActionRestart
|
return pa == PowerActionStart || pa == PowerActionRestart
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if there is currently a power action being processed for the server.
|
// ExecutingPowerAction checks if there is currently a power action being processed for the server.
|
||||||
func (s *Server) ExecutingPowerAction() bool {
|
func (s *Server) ExecutingPowerAction() bool {
|
||||||
if s.powerLock == nil {
|
if s.powerLock == nil {
|
||||||
return false
|
return false
|
||||||
|
@ -54,9 +55,9 @@ func (s *Server) ExecutingPowerAction() bool {
|
||||||
return !ok
|
return !ok
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper function that can receive a power action and then process the actions that need
|
// HandlePowerAction is a helper function that can receive a power action and then process the
|
||||||
// to occur for it. This guards against someone calling Start() twice at the same time, or
|
// actions that need to occur for it. This guards against someone calling Start() twice at the
|
||||||
// trying to restart while another restart process is currently running.
|
// same time, or trying to restart while another restart process is currently running.
|
||||||
//
|
//
|
||||||
// However, the code design for the daemon does depend on the user correctly calling this
|
// However, the code design for the daemon does depend on the user correctly calling this
|
||||||
// function rather than making direct calls to the start/stop/restart functions on the
|
// function rather than making direct calls to the start/stop/restart functions on the
|
||||||
|
@ -107,7 +108,7 @@ func (s *Server) HandlePowerAction(action PowerAction, waitSeconds ...int) error
|
||||||
// Release the lock once the process being requested has finished executing.
|
// Release the lock once the process being requested has finished executing.
|
||||||
defer s.powerLock.Release(1)
|
defer s.powerLock.Release(1)
|
||||||
} else {
|
} else {
|
||||||
// Still try to acquire the lock if terminating and it is available, just so that other power
|
// Still try to acquire the lock if terminating, and it is available, just so that other power
|
||||||
// actions are blocked until it has completed. However, if it is unavailable we won't stop
|
// actions are blocked until it has completed. However, if it is unavailable we won't stop
|
||||||
// the entire process.
|
// the entire process.
|
||||||
if ok := s.powerLock.TryAcquire(1); ok {
|
if ok := s.powerLock.TryAcquire(1); ok {
|
||||||
|
@ -190,14 +191,14 @@ func (s *Server) onBeforeStart() error {
|
||||||
// Update the configuration files defined for the server before beginning the boot process.
|
// Update the configuration files defined for the server before beginning the boot process.
|
||||||
// This process executes a bunch of parallel updates, so we just block until that process
|
// This process executes a bunch of parallel updates, so we just block until that process
|
||||||
// is complete. Any errors as a result of this will just be bubbled out in the logger,
|
// is complete. Any errors as a result of this will just be bubbled out in the logger,
|
||||||
// we don't need to actively do anything about it at this point, worst comes to worst the
|
// we don't need to actively do anything about it at this point, worse comes to worst the
|
||||||
// server starts in a weird state and the user can manually adjust.
|
// server starts in a weird state and the user can manually adjust.
|
||||||
s.PublishConsoleOutputFromDaemon("Updating process configuration files...")
|
s.PublishConsoleOutputFromDaemon("Updating process configuration files...")
|
||||||
s.UpdateConfigurationFiles()
|
s.UpdateConfigurationFiles()
|
||||||
|
|
||||||
if config.Get().System.CheckPermissionsOnBoot {
|
if config.Get().System.CheckPermissionsOnBoot {
|
||||||
s.PublishConsoleOutputFromDaemon("Ensuring file permissions are set correctly, this could take a few seconds...")
|
s.PublishConsoleOutputFromDaemon("Ensuring file permissions are set correctly, this could take a few seconds...")
|
||||||
// Ensure all of the server file permissions are set correctly before booting the process.
|
// Ensure all the server file permissions are set correctly before booting the process.
|
||||||
if err := s.Filesystem().Chown("/"); err != nil {
|
if err := s.Filesystem().Chown("/"); err != nil {
|
||||||
return errors.WithMessage(err, "failed to chown root server directory during pre-boot process")
|
return errors.WithMessage(err, "failed to chown root server directory during pre-boot process")
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@ import (
|
||||||
"github.com/pterodactyl/wings/system"
|
"github.com/pterodactyl/wings/system"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Defines the current resource usage for a given server instance. If a server is offline you
|
// ResourceUsage defines the current resource usage for a given server instance. If a server is offline you
|
||||||
// should obviously expect memory and CPU usage to be 0. However, disk will always be returned
|
// should obviously expect memory and CPU usage to be 0. However, disk will always be returned
|
||||||
// since that is not dependent on the server being running to collect that data.
|
// since that is not dependent on the server being running to collect that data.
|
||||||
type ResourceUsage struct {
|
type ResourceUsage struct {
|
||||||
|
@ -26,7 +26,7 @@ type ResourceUsage struct {
|
||||||
Disk int64 `json:"disk_bytes"`
|
Disk int64 `json:"disk_bytes"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns the current resource usage stats for the server instance. This returns
|
// Proc returns the current resource usage stats for the server instance. This returns
|
||||||
// a copy of the tracked resources, so making any changes to the response will not
|
// a copy of the tracked resources, so making any changes to the response will not
|
||||||
// have the desired outcome for you most likely.
|
// have the desired outcome for you most likely.
|
||||||
func (s *Server) Proc() ResourceUsage {
|
func (s *Server) Proc() ResourceUsage {
|
||||||
|
@ -38,11 +38,12 @@ func (s *Server) Proc() ResourceUsage {
|
||||||
return s.resources
|
return s.resources
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resets the usages values to zero, used when a server is stopped to ensure we don't hold
|
// Reset resets the usages values to zero, used when a server is stopped to ensure we don't hold
|
||||||
// onto any values incorrectly.
|
// onto any values incorrectly.
|
||||||
func (ru *ResourceUsage) Reset() {
|
func (ru *ResourceUsage) Reset() {
|
||||||
ru.mu.Lock()
|
ru.mu.Lock()
|
||||||
defer ru.mu.Unlock()
|
defer ru.mu.Unlock()
|
||||||
|
|
||||||
ru.Memory = 0
|
ru.Memory = 0
|
||||||
ru.CpuAbsolute = 0
|
ru.CpuAbsolute = 0
|
||||||
ru.Network.TxBytes = 0
|
ru.Network.TxBytes = 0
|
||||||
|
|
|
@ -11,6 +11,8 @@ import (
|
||||||
"emperror.dev/errors"
|
"emperror.dev/errors"
|
||||||
"github.com/apex/log"
|
"github.com/apex/log"
|
||||||
"github.com/creasty/defaults"
|
"github.com/creasty/defaults"
|
||||||
|
"golang.org/x/sync/semaphore"
|
||||||
|
|
||||||
"github.com/pterodactyl/wings/config"
|
"github.com/pterodactyl/wings/config"
|
||||||
"github.com/pterodactyl/wings/environment"
|
"github.com/pterodactyl/wings/environment"
|
||||||
"github.com/pterodactyl/wings/environment/docker"
|
"github.com/pterodactyl/wings/environment/docker"
|
||||||
|
@ -18,7 +20,6 @@ import (
|
||||||
"github.com/pterodactyl/wings/remote"
|
"github.com/pterodactyl/wings/remote"
|
||||||
"github.com/pterodactyl/wings/server/filesystem"
|
"github.com/pterodactyl/wings/server/filesystem"
|
||||||
"github.com/pterodactyl/wings/system"
|
"github.com/pterodactyl/wings/system"
|
||||||
"golang.org/x/sync/semaphore"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Server is the high level definition for a server instance being controlled
|
// Server is the high level definition for a server instance being controlled
|
||||||
|
@ -93,8 +94,8 @@ func New(client remote.Client) (*Server, error) {
|
||||||
return &s, nil
|
return &s, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Id returns the UUID for the server instance.
|
// ID returns the UUID for the server instance.
|
||||||
func (s *Server) Id() string {
|
func (s *Server) ID() string {
|
||||||
return s.Config().GetUuid()
|
return s.Config().GetUuid()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -129,7 +130,7 @@ eloop:
|
||||||
for k := range s.Config().EnvVars {
|
for k := range s.Config().EnvVars {
|
||||||
// Don't allow any environment variables that we have already set above.
|
// Don't allow any environment variables that we have already set above.
|
||||||
for _, e := range out {
|
for _, e := range out {
|
||||||
if strings.HasPrefix(e, strings.ToUpper(k) + "=") {
|
if strings.HasPrefix(e, strings.ToUpper(k)+"=") {
|
||||||
continue eloop
|
continue eloop
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -141,7 +142,7 @@ eloop:
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) Log() *log.Entry {
|
func (s *Server) Log() *log.Entry {
|
||||||
return log.WithField("server", s.Id())
|
return log.WithField("server", s.ID())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sync syncs the state of the server on the Panel with Wings. This ensures that
|
// Sync syncs the state of the server on the Panel with Wings. This ensures that
|
||||||
|
@ -151,7 +152,7 @@ func (s *Server) Log() *log.Entry {
|
||||||
// This also means mass actions can be performed against servers on the Panel
|
// This also means mass actions can be performed against servers on the Panel
|
||||||
// and they will automatically sync with Wings when the server is started.
|
// and they will automatically sync with Wings when the server is started.
|
||||||
func (s *Server) Sync() error {
|
func (s *Server) Sync() error {
|
||||||
cfg, err := s.client.GetServerConfiguration(s.Context(), s.Id())
|
cfg, err := s.client.GetServerConfiguration(s.Context(), s.ID())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err := remote.AsRequestError(err); err != nil && err.StatusCode() == http.StatusNotFound {
|
if err := remote.AsRequestError(err); err != nil && err.StatusCode() == http.StatusNotFound {
|
||||||
return &serverDoesNotExist{}
|
return &serverDoesNotExist{}
|
||||||
|
@ -246,7 +247,7 @@ func (s *Server) EnsureDataDirectoryExists() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sets the state of the server internally. This function handles crash detection as
|
// OnStateChange sets the state of the server internally. This function handles crash detection as
|
||||||
// well as reporting to event listeners for the server.
|
// well as reporting to event listeners for the server.
|
||||||
func (s *Server) OnStateChange() {
|
func (s *Server) OnStateChange() {
|
||||||
prevState := s.resources.State.Load()
|
prevState := s.resources.State.Load()
|
||||||
|
@ -261,7 +262,7 @@ func (s *Server) OnStateChange() {
|
||||||
s.Events().Publish(StatusEvent, st)
|
s.Events().Publish(StatusEvent, st)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset the resource usage to 0 when the process fully stops so that all of the UI
|
// Reset the resource usage to 0 when the process fully stops so that all the UI
|
||||||
// views in the Panel correctly display 0.
|
// views in the Panel correctly display 0.
|
||||||
if st == environment.ProcessOfflineState {
|
if st == environment.ProcessOfflineState {
|
||||||
s.resources.Reset()
|
s.resources.Reset()
|
||||||
|
@ -293,7 +294,7 @@ func (s *Server) OnStateChange() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsRunning determines if the server state is running or not. This is different
|
// IsRunning determines if the server state is running or not. This is different
|
||||||
// than the environment state, it is simply the tracked state from this daemon
|
// from the environment state, it is simply the tracked state from this daemon
|
||||||
// instance, and not the response from Docker.
|
// instance, and not the response from Docker.
|
||||||
func (s *Server) IsRunning() bool {
|
func (s *Server) IsRunning() bool {
|
||||||
st := s.Environment.State()
|
st := s.Environment.State()
|
||||||
|
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"emperror.dev/errors"
|
"emperror.dev/errors"
|
||||||
"github.com/buger/jsonparser"
|
"github.com/buger/jsonparser"
|
||||||
"github.com/imdario/mergo"
|
"github.com/imdario/mergo"
|
||||||
|
|
||||||
"github.com/pterodactyl/wings/environment"
|
"github.com/pterodactyl/wings/environment"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -25,7 +26,7 @@ func (s *Server) UpdateDataStructure(data []byte) error {
|
||||||
// Don't allow obviously corrupted data to pass through into this function. If the UUID
|
// Don't allow obviously corrupted data to pass through into this function. If the UUID
|
||||||
// doesn't match something has gone wrong and the API is attempting to meld this server
|
// doesn't match something has gone wrong and the API is attempting to meld this server
|
||||||
// instance into a totally different one, which would be bad.
|
// instance into a totally different one, which would be bad.
|
||||||
if src.Uuid != "" && s.Id() != "" && src.Uuid != s.Id() {
|
if src.Uuid != "" && s.ID() != "" && src.Uuid != s.ID() {
|
||||||
return errors.New("server/update: attempting to merge a data stack with an invalid UUID")
|
return errors.New("server/update: attempting to merge a data stack with an invalid UUID")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@ type WebsocketBag struct {
|
||||||
conns map[uuid.UUID]*context.CancelFunc
|
conns map[uuid.UUID]*context.CancelFunc
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns the websocket bag which contains all of the currently open websocket connections
|
// Websockets returns the websocket bag which contains all the currently open websocket connections
|
||||||
// for the server instance.
|
// for the server instance.
|
||||||
func (s *Server) Websockets() *WebsocketBag {
|
func (s *Server) Websockets() *WebsocketBag {
|
||||||
s.wsBagLocker.Lock()
|
s.wsBagLocker.Lock()
|
||||||
|
@ -25,7 +25,7 @@ func (s *Server) Websockets() *WebsocketBag {
|
||||||
return s.wsBag
|
return s.wsBag
|
||||||
}
|
}
|
||||||
|
|
||||||
// Adds a new websocket connection to the stack.
|
// Push adds a new websocket connection to the end of the stack.
|
||||||
func (w *WebsocketBag) Push(u uuid.UUID, cancel *context.CancelFunc) {
|
func (w *WebsocketBag) Push(u uuid.UUID, cancel *context.CancelFunc) {
|
||||||
w.mu.Lock()
|
w.mu.Lock()
|
||||||
defer w.mu.Unlock()
|
defer w.mu.Unlock()
|
||||||
|
@ -37,14 +37,14 @@ func (w *WebsocketBag) Push(u uuid.UUID, cancel *context.CancelFunc) {
|
||||||
w.conns[u] = cancel
|
w.conns[u] = cancel
|
||||||
}
|
}
|
||||||
|
|
||||||
// Removes a connection from the stack.
|
// Remove removes a connection from the stack.
|
||||||
func (w *WebsocketBag) Remove(u uuid.UUID) {
|
func (w *WebsocketBag) Remove(u uuid.UUID) {
|
||||||
w.mu.Lock()
|
w.mu.Lock()
|
||||||
delete(w.conns, u)
|
delete(w.conns, u)
|
||||||
w.mu.Unlock()
|
w.mu.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cancels all of the stored cancel functions which has the effect of disconnecting
|
// CancelAll cancels all the stored cancel functions which has the effect of disconnecting
|
||||||
// every listening websocket for the server.
|
// every listening websocket for the server.
|
||||||
func (w *WebsocketBag) CancelAll() {
|
func (w *WebsocketBag) CancelAll() {
|
||||||
w.mu.Lock()
|
w.mu.Lock()
|
||||||
|
|
|
@ -11,9 +11,10 @@ import (
|
||||||
"emperror.dev/errors"
|
"emperror.dev/errors"
|
||||||
"github.com/apex/log"
|
"github.com/apex/log"
|
||||||
"github.com/pkg/sftp"
|
"github.com/pkg/sftp"
|
||||||
|
"golang.org/x/crypto/ssh"
|
||||||
|
|
||||||
"github.com/pterodactyl/wings/config"
|
"github.com/pterodactyl/wings/config"
|
||||||
"github.com/pterodactyl/wings/server/filesystem"
|
"github.com/pterodactyl/wings/server/filesystem"
|
||||||
"golang.org/x/crypto/ssh"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|
|
@ -18,10 +18,11 @@ import (
|
||||||
"emperror.dev/errors"
|
"emperror.dev/errors"
|
||||||
"github.com/apex/log"
|
"github.com/apex/log"
|
||||||
"github.com/pkg/sftp"
|
"github.com/pkg/sftp"
|
||||||
|
"golang.org/x/crypto/ssh"
|
||||||
|
|
||||||
"github.com/pterodactyl/wings/config"
|
"github.com/pterodactyl/wings/config"
|
||||||
"github.com/pterodactyl/wings/remote"
|
"github.com/pterodactyl/wings/remote"
|
||||||
"github.com/pterodactyl/wings/server"
|
"github.com/pterodactyl/wings/server"
|
||||||
"golang.org/x/crypto/ssh"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Usernames all follow the same format, so don't even bother hitting the API if the username is not
|
// Usernames all follow the same format, so don't even bother hitting the API if the username is not
|
||||||
|
@ -132,7 +133,7 @@ func (c *SFTPServer) AcceptInbound(conn net.Conn, config *ssh.ServerConfig) {
|
||||||
if uuid == "" {
|
if uuid == "" {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return s.Id() == uuid
|
return s.ID() == uuid
|
||||||
})
|
})
|
||||||
if srv == nil {
|
if srv == nil {
|
||||||
continue
|
continue
|
||||||
|
|
Loading…
Reference in New Issue
Block a user