Compare commits
11 Commits
v1.0.0-bet
...
v1.0.0-bet
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4c3d4f112b | ||
|
|
d6a3d9adb1 | ||
|
|
d284c4aec9 | ||
|
|
05a4730489 | ||
|
|
2dad3102e0 | ||
|
|
b33f14ddd9 | ||
|
|
1f6789cba3 | ||
|
|
073247e4e1 | ||
|
|
a3d83d23bd | ||
|
|
f318962371 | ||
|
|
db31722cfc |
12
api/api.go
12
api/api.go
@@ -130,6 +130,12 @@ func (r *PanelRequest) HttpResponseCode() int {
|
||||
return r.Response.StatusCode
|
||||
}
|
||||
|
||||
func IsRequestError(err error) bool {
|
||||
_, ok := err.(*RequestError)
|
||||
|
||||
return ok
|
||||
}
|
||||
|
||||
type RequestError struct {
|
||||
Code string `json:"code"`
|
||||
Status string `json:"status"`
|
||||
@@ -137,10 +143,14 @@ type RequestError struct {
|
||||
}
|
||||
|
||||
// Returns the error response in a string form that can be more easily consumed.
|
||||
func (re *RequestError) String() string {
|
||||
func (re *RequestError) Error() string {
|
||||
return fmt.Sprintf("%s: %s (HTTP/%s)", re.Code, re.Detail, re.Status)
|
||||
}
|
||||
|
||||
func (re *RequestError) String() string {
|
||||
return re.Error()
|
||||
}
|
||||
|
||||
type RequestErrorBag struct {
|
||||
Errors []RequestError `json:"errors"`
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ import (
|
||||
"fmt"
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/AlecAivazis/survey/v2/terminal"
|
||||
"github.com/creasty/defaults"
|
||||
"github.com/pterodactyl/wings/config"
|
||||
"github.com/spf13/cobra"
|
||||
"io/ioutil"
|
||||
@@ -147,8 +146,8 @@ func configureCmdRun(cmd *cobra.Command, args []string) {
|
||||
|
||||
b, err := ioutil.ReadAll(res.Body)
|
||||
|
||||
cfg := new(config.Configuration)
|
||||
if err := defaults.Set(cfg); err != nil {
|
||||
cfg, err := config.NewFromPath(configPath)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
|
||||
@@ -132,7 +132,7 @@ func ReadConfiguration(path string) (*Configuration, error) {
|
||||
}
|
||||
|
||||
// Track the location where we created this configuration.
|
||||
c.path = path
|
||||
c.unsafeSetPath(path)
|
||||
|
||||
// Replace environment variables within the configuration file with their
|
||||
// values from the host system.
|
||||
@@ -186,8 +186,32 @@ func GetJwtAlgorithm() *jwt.HMACSHA {
|
||||
return _jwtAlgo
|
||||
}
|
||||
|
||||
// Create a new struct and set the path where it should be stored.
|
||||
func NewFromPath(path string) (*Configuration, error) {
|
||||
c := new(Configuration)
|
||||
if err := defaults.Set(c); err != nil {
|
||||
return c, err
|
||||
}
|
||||
|
||||
c.unsafeSetPath(path)
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// Sets the path where the configuration file is located on the server. This function should
|
||||
// not be called except by processes that are generating the configuration such as the configration
|
||||
// command shipped with this software.
|
||||
func (c *Configuration) unsafeSetPath(path string) {
|
||||
c.Lock()
|
||||
c.path = path
|
||||
c.Unlock()
|
||||
}
|
||||
|
||||
// Returns the path for this configuration file.
|
||||
func (c *Configuration) GetPath() string {
|
||||
c.RLock()
|
||||
defer c.RUnlock()
|
||||
|
||||
return c.path
|
||||
}
|
||||
|
||||
@@ -245,11 +269,10 @@ func (c *Configuration) setSystemUser(u *user.User) error {
|
||||
gid, _ := strconv.Atoi(u.Gid)
|
||||
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
|
||||
c.System.Username = u.Username
|
||||
c.System.User.Uid = uid
|
||||
c.System.User.Gid = gid
|
||||
c.Unlock()
|
||||
|
||||
return c.WriteToDisk()
|
||||
}
|
||||
@@ -310,6 +333,10 @@ func (c *Configuration) EnsureFilePermissions() error {
|
||||
// lock on the file. This prevents something else from writing at the exact same time and
|
||||
// leading to bad data conditions.
|
||||
func (c *Configuration) WriteToDisk() error {
|
||||
// Obtain an exclusive write against the configuration file.
|
||||
c.writeLock.Lock()
|
||||
defer c.writeLock.Unlock()
|
||||
|
||||
ccopy := *c
|
||||
// If debugging is set with the flag, don't save that to the configuration file, otherwise
|
||||
// you'll always end up in debug mode.
|
||||
@@ -326,10 +353,6 @@ func (c *Configuration) WriteToDisk() error {
|
||||
return err
|
||||
}
|
||||
|
||||
// Obtain an exclusive write against the configuration file.
|
||||
c.writeLock.Lock()
|
||||
defer c.writeLock.Unlock()
|
||||
|
||||
if err := ioutil.WriteFile(c.GetPath(), b, 0644); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -70,6 +70,8 @@ func (h *Handler) HandleLog(e *log.Entry) error {
|
||||
}
|
||||
|
||||
if err, ok := e.Fields.Get("error").(error); ok {
|
||||
var br = color2.New(color2.Bold, color2.FgRed)
|
||||
|
||||
if e, ok := errors.Cause(err).(tracer); ok {
|
||||
st := e.StackTrace()
|
||||
|
||||
@@ -78,11 +80,9 @@ func (h *Handler) HandleLog(e *log.Entry) error {
|
||||
l = 5
|
||||
}
|
||||
|
||||
br := color2.New(color2.Bold, color2.FgRed)
|
||||
|
||||
fmt.Fprintf(h.Writer, "\n%s%+v\n\n", br.Sprintf("Stacktrace:"), st[0:l])
|
||||
} else {
|
||||
fmt.Printf("\n\nINVALID TRACER\n\n")
|
||||
fmt.Fprintf(h.Writer, "\n%s\n%+v\n\n", br.Sprintf("Stacktrace:"), err)
|
||||
}
|
||||
} else {
|
||||
fmt.Printf("\n\nINVALID ERROR\n\n")
|
||||
|
||||
@@ -96,12 +96,12 @@ func (f *ConfigurationFile) IterateOverJson(data []byte) (*gabs.Container, error
|
||||
// If the child is a null value, nothing will happen. Seems reasonable as of the
|
||||
// time this code is being written.
|
||||
for _, child := range parsed.Path(strings.Trim(parts[0], ".")).Children() {
|
||||
if err := v.SetAtPathway(child, strings.Trim(parts[1], "."), value); err != nil {
|
||||
if err := v.SetAtPathway(child, strings.Trim(parts[1], "."), []byte(value)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if err = v.SetAtPathway(parsed, v.Match, value); err != nil {
|
||||
if err = v.SetAtPathway(parsed, v.Match, []byte(value)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
@@ -149,12 +149,12 @@ func (cfr *ConfigurationFileReplacement) SetAtPathway(c *gabs.Container, path st
|
||||
}
|
||||
|
||||
// Looks up a configuration value on the Daemon given a dot-notated syntax.
|
||||
func (f *ConfigurationFile) LookupConfigurationValue(cfr ConfigurationFileReplacement) ([]byte, error) {
|
||||
func (f *ConfigurationFile) LookupConfigurationValue(cfr ConfigurationFileReplacement) (string, error) {
|
||||
// If this is not something that we can do a regex lookup on then just continue
|
||||
// on our merry way. If the value isn't a string, we're not going to be doing anything
|
||||
// with it anyways.
|
||||
if cfr.ReplaceWith.Type() != jsonparser.String || !configMatchRegex.Match(cfr.ReplaceWith.Value()) {
|
||||
return cfr.ReplaceWith.Value(), nil
|
||||
return cfr.ReplaceWith.String(), nil
|
||||
}
|
||||
|
||||
// If there is a match, lookup the value in the configuration for the Daemon. If no key
|
||||
@@ -174,17 +174,15 @@ func (f *ConfigurationFile) LookupConfigurationValue(cfr ConfigurationFileReplac
|
||||
match, _, _, err := jsonparser.Get(f.configuration, path...)
|
||||
if err != nil {
|
||||
if err != jsonparser.KeyPathNotFoundError {
|
||||
return match, errors.WithStack(err)
|
||||
return string(match), errors.WithStack(err)
|
||||
}
|
||||
|
||||
log.WithFields(log.Fields{"path": path, "filename": f.FileName}).Debug("attempted to load a configuration value that does not exist")
|
||||
|
||||
// If there is no key, keep the original value intact, that way it is obvious there
|
||||
// is a replace issue at play.
|
||||
return match, nil
|
||||
return string(match), nil
|
||||
} else {
|
||||
replaced := []byte(configMatchRegex.ReplaceAllString(cfr.ReplaceWith.String(), string(match)))
|
||||
|
||||
return replaced, nil
|
||||
return configMatchRegex.ReplaceAllString(cfr.ReplaceWith.String(), string(match)), nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -236,13 +236,13 @@ func (f *ConfigurationFile) parseXmlFile(path string) error {
|
||||
|
||||
// Iterate over the elements we found and update their values.
|
||||
for _, element := range doc.FindElements(path) {
|
||||
if xmlValueMatchRegex.Match(value) {
|
||||
k := xmlValueMatchRegex.ReplaceAllString(string(value), "$1")
|
||||
v := xmlValueMatchRegex.ReplaceAllString(string(value), "$2")
|
||||
if xmlValueMatchRegex.MatchString(value) {
|
||||
k := xmlValueMatchRegex.ReplaceAllString(value, "$1")
|
||||
v := xmlValueMatchRegex.ReplaceAllString(value, "$2")
|
||||
|
||||
element.CreateAttr(k, v)
|
||||
} else {
|
||||
element.SetText(string(value))
|
||||
element.SetText(value)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -273,12 +273,13 @@ func (f *ConfigurationFile) parseXmlFile(path string) error {
|
||||
// Parses an ini file.
|
||||
func (f *ConfigurationFile) parseIniFile(path string) error {
|
||||
// Ini package can't handle a non-existent file, so handle that automatically here
|
||||
// by creating it if not exists.
|
||||
// by creating it if not exists. Then, immediately close the file since we will use
|
||||
// other methods to write the new contents.
|
||||
file, err := os.OpenFile(path, os.O_CREATE|os.O_RDWR, 0644)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
file.Close()
|
||||
|
||||
cfg, err := ini.Load(path)
|
||||
if err != nil {
|
||||
@@ -313,24 +314,15 @@ func (f *ConfigurationFile) parseIniFile(path string) error {
|
||||
// If the key exists in the file go ahead and set the value, otherwise try to
|
||||
// create it in the section.
|
||||
if s.HasKey(k) {
|
||||
s.Key(k).SetValue(string(value))
|
||||
s.Key(k).SetValue(value)
|
||||
} else {
|
||||
if _, err := s.NewKey(k, string(value)); err != nil {
|
||||
if _, err := s.NewKey(k, value); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Truncate the file before attempting to write the changes.
|
||||
if err := os.Truncate(path, 0); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := cfg.WriteTo(file); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
return cfg.SaveTo(path)
|
||||
}
|
||||
|
||||
// Parses a json file updating any matching key/value pairs. If a match is not found, the
|
||||
@@ -452,7 +444,7 @@ func (f *ConfigurationFile) parsePropertiesFile(path string) error {
|
||||
continue
|
||||
}
|
||||
|
||||
if _, _, err := p.Set(replace.Match, string(data)); err != nil {
|
||||
if _, _, err := p.Set(replace.Match, data); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,9 @@ func (cv *ReplaceValue) Value() []byte {
|
||||
}
|
||||
|
||||
func (cv *ReplaceValue) String() string {
|
||||
return string(cv.value)
|
||||
str, _ := jsonparser.ParseString(cv.value)
|
||||
|
||||
return str
|
||||
}
|
||||
|
||||
func (cv *ReplaceValue) Type() jsonparser.ValueType {
|
||||
|
||||
@@ -164,6 +164,11 @@ func deleteServer(c *gin.Context) {
|
||||
// to start it while this process is running.
|
||||
s.Suspended = true
|
||||
|
||||
// If the server is currently installing, abort it.
|
||||
if s.IsInstalling() {
|
||||
s.AbortInstallation()
|
||||
}
|
||||
|
||||
// Delete the server's archive if it exists. We intentionally don't return
|
||||
// here, if the archive fails to delete, the server can still be removed.
|
||||
if err := s.Archiver.DeleteIfExists(); err != nil {
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"github.com/pterodactyl/wings/server"
|
||||
"github.com/pterodactyl/wings/system"
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Returns information about the system that wings is running on.
|
||||
@@ -78,6 +79,16 @@ func postUpdateConfiguration(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
// Keep the SSL certificates the same since the Panel will send through Lets Encrypt
|
||||
// default locations. However, if we picked a different location manually we don't
|
||||
// want to override that.
|
||||
//
|
||||
// If you pass through manual locations in the API call this logic will be skipped.
|
||||
if strings.HasPrefix(cfg.Api.Ssl.KeyFile, "/etc/letsencrypt/live/") {
|
||||
cfg.Api.Ssl.KeyFile = ccopy.Api.Ssl.KeyFile
|
||||
cfg.Api.Ssl.CertificateFile = ccopy.Api.Ssl.CertificateFile
|
||||
}
|
||||
|
||||
config.Set(&cfg)
|
||||
if err := config.Get().WriteToDisk(); err != nil {
|
||||
// If there was an error writing to the disk, revert back to the configuration we had
|
||||
|
||||
@@ -564,7 +564,10 @@ func (d *DockerEnvironment) DisableResourcePolling() error {
|
||||
//
|
||||
// @todo handle authorization & local images
|
||||
func (d *DockerEnvironment) ensureImageExists(c *client.Client) error {
|
||||
ctx, _ := context.WithTimeout(context.Background(), time.Second*10)
|
||||
// Give it up to 15 minutes to pull the image. I think this should cover 99.8% of cases where an
|
||||
// image pull might fail. I can't imagine it will ever take more than 15 minutes to fully pull
|
||||
// an image. Let me know when I am inevitably wrong here...
|
||||
ctx, _ := context.WithTimeout(context.Background(), time.Minute*15)
|
||||
|
||||
out, err := c.ImagePull(ctx, d.Server.Container.Image, types.ImagePullOptions{All: false})
|
||||
if err != nil {
|
||||
|
||||
@@ -12,12 +12,14 @@ import (
|
||||
"github.com/pkg/errors"
|
||||
"github.com/pterodactyl/wings/api"
|
||||
"github.com/pterodactyl/wings/config"
|
||||
"golang.org/x/sync/semaphore"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Executes the installation stack for a server process. Bubbles any errors up to the calling
|
||||
@@ -27,10 +29,16 @@ func (s *Server) Install() error {
|
||||
|
||||
s.Log().Debug("notifying panel of server install state")
|
||||
if serr := s.SyncInstallState(err == nil); serr != nil {
|
||||
s.Log().WithFields(log.Fields{
|
||||
"was_successful": err == nil,
|
||||
"error": serr,
|
||||
}).Warn("failed to notify panel of server install state")
|
||||
l := s.Log().WithField("was_successful", err == nil)
|
||||
|
||||
// If the request was successful but there was an error with this request, attach the
|
||||
// error to this log entry. Otherwise ignore it in this log since whatever is calling
|
||||
// this function should handle the error and will end up logging the same one.
|
||||
if err == nil {
|
||||
l.WithField("error", serr)
|
||||
}
|
||||
|
||||
l.Warn("failed to notify panel of server install state")
|
||||
}
|
||||
|
||||
return err
|
||||
@@ -79,7 +87,7 @@ type InstallationProcess struct {
|
||||
Script *api.InstallationScript
|
||||
|
||||
client *client.Client
|
||||
mutex *sync.Mutex
|
||||
context context.Context
|
||||
}
|
||||
|
||||
// Generates a new installation process struct that will be used to create containers,
|
||||
@@ -88,21 +96,70 @@ func NewInstallationProcess(s *Server, script *api.InstallationScript) (*Install
|
||||
proc := &InstallationProcess{
|
||||
Script: script,
|
||||
Server: s,
|
||||
mutex: &sync.Mutex{},
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
s.installer.cancel = &cancel
|
||||
|
||||
if c, err := client.NewClientWithOpts(client.FromEnv); err != nil {
|
||||
return nil, errors.WithStack(err)
|
||||
} else {
|
||||
proc.client = c
|
||||
proc.context = ctx
|
||||
}
|
||||
|
||||
return proc, nil
|
||||
}
|
||||
|
||||
// Try to obtain an exclusive lock on the installation process for the server. Waits up to 10
|
||||
// seconds before aborting with a context timeout.
|
||||
func (s *Server) acquireInstallationLock() error {
|
||||
if s.installer.sem == nil {
|
||||
s.installer.sem = semaphore.NewWeighted(1)
|
||||
}
|
||||
|
||||
ctx, _ := context.WithTimeout(context.Background(), time.Second*10)
|
||||
|
||||
return s.installer.sem.Acquire(ctx, 1)
|
||||
}
|
||||
|
||||
// Determines if the server is actively running the installation process by checking the status
|
||||
// of the semaphore lock.
|
||||
func (s *Server) IsInstalling() bool {
|
||||
if s.installer.sem == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if s.installer.sem.TryAcquire(1) {
|
||||
// If we made it into this block it means we were able to obtain an exclusive lock
|
||||
// on the semaphore. In that case, go ahead and release that lock immediately, and
|
||||
// return false.
|
||||
s.installer.sem.Release(1)
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// Aborts the server installation process by calling the cancel function on the installer
|
||||
// context.
|
||||
func (s *Server) AbortInstallation() {
|
||||
if !s.IsInstalling() {
|
||||
return
|
||||
}
|
||||
|
||||
if s.installer.cancel != nil {
|
||||
cancel := *s.installer.cancel
|
||||
|
||||
s.Log().Warn("aborting running installation process")
|
||||
cancel()
|
||||
}
|
||||
}
|
||||
|
||||
// Removes the installer container for the server.
|
||||
func (ip *InstallationProcess) RemoveContainer() {
|
||||
err := ip.client.ContainerRemove(context.Background(), ip.Server.Uuid+"_installer", types.ContainerRemoveOptions{
|
||||
err := ip.client.ContainerRemove(ip.context, ip.Server.Uuid+"_installer", types.ContainerRemoveOptions{
|
||||
RemoveVolumes: true,
|
||||
Force: true,
|
||||
})
|
||||
@@ -118,6 +175,20 @@ func (ip *InstallationProcess) RemoveContainer() {
|
||||
// Once the container finishes installing the results will be stored in an installation
|
||||
// log in the server's configuration directory.
|
||||
func (ip *InstallationProcess) Run() error {
|
||||
ip.Server.Log().Debug("acquiring installation process lock")
|
||||
if err := ip.Server.acquireInstallationLock(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// We now have an exclusive lock on this installation process. Ensure that whenever this
|
||||
// process is finished that the semaphore is released so that other processes and be executed
|
||||
// without encounting a wait timeout.
|
||||
defer func() {
|
||||
ip.Server.Log().Debug("releasing installation process lock")
|
||||
ip.Server.installer.sem.Release(1)
|
||||
ip.Server.installer.cancel = nil
|
||||
}()
|
||||
|
||||
installPath, err := ip.BeforeExecute()
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -177,7 +248,7 @@ func (ip *InstallationProcess) writeScriptToDisk() (string, error) {
|
||||
|
||||
// Pulls the docker image to be used for the installation container.
|
||||
func (ip *InstallationProcess) pullInstallationImage() error {
|
||||
r, err := ip.client.ImagePull(context.Background(), ip.Script.ContainerImage, types.ImagePullOptions{})
|
||||
r, err := ip.client.ImagePull(ip.context, ip.Script.ContainerImage, types.ImagePullOptions{})
|
||||
if err != nil {
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
@@ -231,7 +302,7 @@ func (ip *InstallationProcess) BeforeExecute() (string, error) {
|
||||
Force: true,
|
||||
}
|
||||
|
||||
if err := ip.client.ContainerRemove(context.Background(), ip.Server.Uuid+"_installer", opts); err != nil {
|
||||
if err := ip.client.ContainerRemove(ip.context, ip.Server.Uuid+"_installer", opts); err != nil {
|
||||
if !client.IsErrNotFound(err) {
|
||||
e = append(e, err)
|
||||
}
|
||||
@@ -258,11 +329,10 @@ func (ip *InstallationProcess) GetLogPath() string {
|
||||
// process to store in the server configuration directory, and then destroys the associated
|
||||
// installation container.
|
||||
func (ip *InstallationProcess) AfterExecute(containerId string) error {
|
||||
ctx := context.Background()
|
||||
defer ip.RemoveContainer()
|
||||
|
||||
ip.Server.Log().WithField("container_id", containerId).Debug("pulling installation logs for server")
|
||||
reader, err := ip.client.ContainerLogs(ctx, containerId, types.ContainerLogsOptions{
|
||||
reader, err := ip.client.ContainerLogs(ip.context, containerId, types.ContainerLogsOptions{
|
||||
ShowStdout: true,
|
||||
ShowStderr: true,
|
||||
Follow: false,
|
||||
@@ -289,8 +359,6 @@ func (ip *InstallationProcess) AfterExecute(containerId string) error {
|
||||
|
||||
// Executes the installation process inside a specially created docker container.
|
||||
func (ip *InstallationProcess) Execute(installPath string) (string, error) {
|
||||
ctx := context.Background()
|
||||
|
||||
conf := &container.Config{
|
||||
Hostname: "installer",
|
||||
AttachStdout: true,
|
||||
@@ -339,13 +407,13 @@ func (ip *InstallationProcess) Execute(installPath string) (string, error) {
|
||||
}
|
||||
|
||||
ip.Server.Log().WithField("install_script", installPath+"/install.sh").Info("creating install container for server process")
|
||||
r, err := ip.client.ContainerCreate(ctx, conf, hostConf, nil, ip.Server.Uuid+"_installer")
|
||||
r, err := ip.client.ContainerCreate(ip.context, conf, hostConf, nil, ip.Server.Uuid+"_installer")
|
||||
if err != nil {
|
||||
return "", errors.WithStack(err)
|
||||
}
|
||||
|
||||
ip.Server.Log().WithField("container_id", r.ID).Info("running installation script for server in container")
|
||||
if err := ip.client.ContainerStart(ctx, r.ID, types.ContainerStartOptions{}); err != nil {
|
||||
if err := ip.client.ContainerStart(ip.context, r.ID, types.ContainerStartOptions{}); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
@@ -357,7 +425,7 @@ func (ip *InstallationProcess) Execute(installPath string) (string, error) {
|
||||
ip.Server.Events().Publish(DaemonMessageEvent, "Installation process completed.")
|
||||
}(r.ID)
|
||||
|
||||
sChann, eChann := ip.client.ContainerWait(ctx, r.ID, container.WaitConditionNotRunning)
|
||||
sChann, eChann := ip.client.ContainerWait(ip.context, r.ID, container.WaitConditionNotRunning)
|
||||
select {
|
||||
case err := <-eChann:
|
||||
if err != nil {
|
||||
@@ -373,7 +441,7 @@ func (ip *InstallationProcess) Execute(installPath string) (string, error) {
|
||||
// directory, as well as to a websocket listener so that the process can be viewed in
|
||||
// the panel by administrators.
|
||||
func (ip *InstallationProcess) StreamOutput(id string) error {
|
||||
reader, err := ip.client.ContainerLogs(context.Background(), id, types.ContainerLogsOptions{
|
||||
reader, err := ip.client.ContainerLogs(ip.context, id, types.ContainerLogsOptions{
|
||||
ShowStdout: true,
|
||||
ShowStderr: true,
|
||||
Follow: true,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/apex/log"
|
||||
"github.com/creasty/defaults"
|
||||
@@ -9,6 +10,7 @@ import (
|
||||
"github.com/pterodactyl/wings/api"
|
||||
"github.com/pterodactyl/wings/config"
|
||||
"github.com/remeh/sizedwaitgroup"
|
||||
"golang.org/x/sync/semaphore"
|
||||
"math"
|
||||
"os"
|
||||
"strings"
|
||||
@@ -71,11 +73,27 @@ type Server struct {
|
||||
// started, and then cached here.
|
||||
processConfiguration *api.ProcessConfiguration
|
||||
|
||||
// Tracks the installation process for this server and prevents a server from running
|
||||
// two installer processes at the same time. This also allows us to cancel a running
|
||||
// installation process, for example when a server is deleted from the panel while the
|
||||
// installer process is still running.
|
||||
installer InstallerDetails
|
||||
|
||||
// Internal mutex used to block actions that need to occur sequentially, such as
|
||||
// writing the configuration to the disk.
|
||||
sync.RWMutex
|
||||
}
|
||||
|
||||
type InstallerDetails struct {
|
||||
// The cancel function for the installer. This will be a non-nil value while there
|
||||
// is an installer running for the server.
|
||||
cancel *context.CancelFunc
|
||||
|
||||
// Installer lock. You should obtain an exclusive lock on this context while running
|
||||
// the installation process and release it when finished.
|
||||
sem *semaphore.Weighted
|
||||
}
|
||||
|
||||
// The build settings for a given server that impact docker container creation and
|
||||
// resource limits for a server instance.
|
||||
type BuildSettings struct {
|
||||
@@ -247,10 +265,6 @@ func FromConfiguration(data *api.ServerConfigurationResponse) (*Server, error) {
|
||||
}
|
||||
s.Resources = ResourceUsage{}
|
||||
|
||||
// Force the disk usage to become cached to return in a resources response
|
||||
// or when connecting to the websocket of an offline server.
|
||||
go s.Filesystem.HasSpaceAvailable()
|
||||
|
||||
// Forces the configuration to be synced with the panel.
|
||||
if err := s.SyncWithConfiguration(data); err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -2,5 +2,5 @@ package system
|
||||
|
||||
const (
|
||||
// The current version of this software.
|
||||
Version = "0.0.1"
|
||||
Version = "1.0.0-beta.7"
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user