From 1d36811dfe72c77d0972fdb550db1f35cf734669 Mon Sep 17 00:00:00 2001 From: Matthew Penner Date: Thu, 7 Jan 2021 16:44:09 -0700 Subject: [PATCH 1/4] Fix v being shown twice on wings boot --- cmd/root.go | 2 +- system/const.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/root.go b/cmd/root.go index 79b7439..ef7d8c8 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -406,7 +406,7 @@ __ [blue][bold]Pterodactyl[reset] _____/___/_______ _______ ______ \_____\ \/\/ / / / __ / ___/ \___\ / / / / /_/ /___ / \___/\___/___/___/___/___ /______/ - /_______/ [bold]v%s[reset] + /_______/ [bold]%s[reset] Copyright © 2018 - 2021 Dane Everitt & Contributors diff --git a/system/const.go b/system/const.go index 87d87fb..21fe42b 100644 --- a/system/const.go +++ b/system/const.go @@ -2,5 +2,5 @@ package system var ( // The current version of this software. - Version = "0.0.1" + Version = "v0.0.1" ) From 66c9be357c5944f167a4c4e041c89f0bc4bda33e Mon Sep 17 00:00:00 2001 From: Matthew Penner Date: Thu, 7 Jan 2021 19:32:15 -0700 Subject: [PATCH 2/4] Potential fix for servers being marked as stopping after being marked as offline --- environment/docker/container.go | 10 ++++++---- server/listeners.go | 2 ++ 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/environment/docker/container.go b/environment/docker/container.go index 58a728d..914ed0b 100644 --- a/environment/docker/container.go +++ b/environment/docker/container.go @@ -60,8 +60,10 @@ func (e *Environment) Attach() error { go func() { ctx, cancel := context.WithCancel(context.Background()) defer cancel() + + defer e.stream.Close() + defer func() { - e.stream.Close() e.SetState(environment.ProcessOfflineState) e.SetStream(nil) }() @@ -78,7 +80,7 @@ func (e *Environment) Attach() error { // Block the completion of this routine until the container is no longer running. This allows // the pollResources function to run until it needs to be stopped. Because the container - // can be polled for resource usage, even when sropped, we need to have this logic present + // can be polled for resource usage, even when stopped, we need to have this logic present // in order to cancel the context and therefore stop the routine that is spawned. ok, err := e.client.ContainerWait(ctx, e.Id, container.WaitConditionNotRunning) select { @@ -272,6 +274,8 @@ func (e *Environment) Destroy() error { Force: true, }) + e.SetState(environment.ProcessOfflineState) + // Don't trigger a destroy failure if we try to delete a container that does not // exist on the system. We're just a step ahead of ourselves in that case. // @@ -280,8 +284,6 @@ func (e *Environment) Destroy() error { return nil } - e.SetState(environment.ProcessOfflineState) - return err } diff --git a/server/listeners.go b/server/listeners.go index 9717879..8318a75 100644 --- a/server/listeners.go +++ b/server/listeners.go @@ -64,9 +64,11 @@ func (s *Server) StartEventListeners() { // to terminate again. if s.Environment.State() != environment.ProcessStoppingState { s.Environment.SetState(environment.ProcessStoppingState) + go func() { s.Log().Warn("stopping server instance, violating throttle limits") s.PublishConsoleOutputFromDaemon("Your server is being stopped for outputting too much data in a short period of time.") + // Completely skip over server power actions and terminate the running instance. This gives the // server 15 seconds to finish stopping gracefully before it is forcefully terminated. if err := s.Environment.WaitForStop(config.Get().Throttles.StopGracePeriod, true); err != nil { From 4f4b4fd2e619a76a347b5de052e244e71905da11 Mon Sep 17 00:00:00 2001 From: Matthew Penner Date: Fri, 8 Jan 2021 08:15:19 -0700 Subject: [PATCH 3/4] environment(docker): cleanup code --- environment/docker/container.go | 11 +++++++++-- environment/docker/environment.go | 10 +++++++--- environment/docker/power.go | 8 ++++---- environment/docker/stream.go | 5 +++-- 4 files changed, 23 insertions(+), 11 deletions(-) diff --git a/environment/docker/container.go b/environment/docker/container.go index 914ed0b..9effb29 100644 --- a/environment/docker/container.go +++ b/environment/docker/container.go @@ -309,16 +309,21 @@ func (e *Environment) followOutput() error { if err != nil { return err } + go func(reader io.ReadCloser) { defer reader.Close() - evts := e.Events() + + events := e.Events() + err := system.ScanReader(reader, func(line string) { - evts.Publish(environment.ConsoleOutputEvent, line) + events.Publish(environment.ConsoleOutputEvent, line) }) + if err != nil && err != io.EOF { log.WithField("error", err).WithField("container_id", e.Id).Warn("error processing scanner line in console output") } }(reader) + return nil } @@ -405,9 +410,11 @@ func (e *Environment) ensureImageExists(image string) error { // I'm not sure what the best approach here is, but this will block execution until the image // is done being pulled, which is what we need. scanner := bufio.NewScanner(out) + for scanner.Scan() { s := imagePullStatus{} fmt.Println(scanner.Text()) + if err := json.Unmarshal(scanner.Bytes(), &s); err == nil { e.Events().Publish(environment.DockerImagePullStatus, s.Status+" "+s.Progress) } diff --git a/environment/docker/environment.go b/environment/docker/environment.go index 535640a..6b1762d 100644 --- a/environment/docker/environment.go +++ b/environment/docker/environment.go @@ -82,8 +82,9 @@ func (e *Environment) Type() string { // Set if this process is currently attached to the process. func (e *Environment) SetStream(s *types.HijackedResponse) { e.mu.Lock() + defer e.mu.Unlock() + e.stream = s - e.mu.Unlock() } // Determine if the this process is currently attached to the container. @@ -98,6 +99,7 @@ func (e *Environment) Events() *events.EventBus { e.eventMu.Do(func() { e.emitter = events.New() }) + return e.emitter } @@ -174,12 +176,14 @@ func (e *Environment) Config() *environment.Configuration { // Sets the stop configuration for the environment. func (e *Environment) SetStopConfiguration(c api.ProcessStopConfiguration) { e.mu.Lock() + defer e.mu.Unlock() + e.meta.Stop = c - e.mu.Unlock() } func (e *Environment) SetImage(i string) { e.mu.Lock() + defer e.mu.Unlock() + e.meta.Image = i - e.mu.Unlock() } diff --git a/environment/docker/power.go b/environment/docker/power.go index 980c596..0663bab 100644 --- a/environment/docker/power.go +++ b/environment/docker/power.go @@ -20,10 +20,9 @@ import ( // // This process will also confirm that the server environment exists and is in a bootable // state. This ensures that unexpected container deletion while Wings is running does -// not result in the server becoming unbootable. +// not result in the server becoming un-bootable. func (e *Environment) OnBeforeStart() error { - // Always destroy and re-create the server container to ensure that synced data from - // the Panel is usee. + // Always destroy and re-create the server container to ensure that synced data from the Panel is used. if err := e.client.ContainerRemove(context.Background(), e.Id, types.ContainerRemoveOptions{RemoveVolumes: true}); err != nil { if !client.IsErrNotFound(err) { return errors.WithMessage(err, "failed to remove server docker container during pre-boot") @@ -49,6 +48,7 @@ func (e *Environment) OnBeforeStart() error { // call to OnBeforeStart(). func (e *Environment) Start() error { sawError := false + // If sawError is set to true there was an error somewhere in the pipeline that // got passed up, but we also want to ensure we set the server to be offline at // that point. @@ -235,7 +235,7 @@ func (e *Environment) Terminate(signal os.Signal) error { sig := strings.TrimSuffix(strings.TrimPrefix(signal.String(), "signal "), "ed") - if err := e.client.ContainerKill(context.Background(), e.Id, sig); err != nil { + if err := e.client.ContainerKill(context.Background(), e.Id, sig); err != nil && !client.IsErrNotFound(err) { return err } diff --git a/environment/docker/stream.go b/environment/docker/stream.go index f387609..ff8725b 100644 --- a/environment/docker/stream.go +++ b/environment/docker/stream.go @@ -19,8 +19,9 @@ var ErrNotAttached = errors.New("not attached to instance") func (e *Environment) setStream(s *types.HijackedResponse) { e.mu.Lock() + defer e.mu.Unlock() + e.stream = s - e.mu.Unlock() } // Sends the specified command to the stdin of the running container instance. There is no @@ -71,7 +72,7 @@ func (e *Environment) Readlog(lines int) ([]string, error) { // Docker stores the logs for server output in a JSON format. This function will iterate over the JSON // that was read from the log file and parse it into a more human readable format. func (e *Environment) parseLogToStrings(b []byte) ([]string, error) { - var hasError = false + hasError := false var out []string scanner := bufio.NewScanner(bytes.NewReader(b)) From 0aab4b1ac2ba9c08b351aa0c0340b887eee7f060 Mon Sep 17 00:00:00 2001 From: Matthew Penner Date: Fri, 8 Jan 2021 08:19:33 -0700 Subject: [PATCH 4/4] environment(docker): re-attach to container logs after EOF --- environment/docker/container.go | 41 ++++++++++++++++++++++----------- 1 file changed, 28 insertions(+), 13 deletions(-) diff --git a/environment/docker/container.go b/environment/docker/container.go index 9effb29..1661e1e 100644 --- a/environment/docker/container.go +++ b/environment/docker/container.go @@ -310,23 +310,38 @@ func (e *Environment) followOutput() error { return err } - go func(reader io.ReadCloser) { - defer reader.Close() - - events := e.Events() - - err := system.ScanReader(reader, func(line string) { - events.Publish(environment.ConsoleOutputEvent, line) - }) - - if err != nil && err != io.EOF { - log.WithField("error", err).WithField("container_id", e.Id).Warn("error processing scanner line in console output") - } - }(reader) + go e.scanOutput(reader) return nil } +func (e *Environment) scanOutput(reader io.ReadCloser) { + defer reader.Close() + + events := e.Events() + + err := system.ScanReader(reader, func(line string) { + events.Publish(environment.ConsoleOutputEvent, line) + }) + + if err != nil && err != io.EOF { + log.WithField("error", err).WithField("container_id", e.Id).Warn("error processing scanner line in console output") + return + } + + // Return here if the server is offline or currently stopping. + if e.State() == environment.ProcessStoppingState || e.State() == environment.ProcessOfflineState { + return + } + + // 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. + _ = reader.Close() + + // Start following the output of the server again. + go e.followOutput() +} + // Pulls the image from Docker. If there is an error while pulling the image from the source // but the image already exists locally, we will report that error to the logger but continue // with the process.