diff --git a/server/environment_docker.go b/server/environment_docker.go index a84e5d6..b75ed41 100644 --- a/server/environment_docker.go +++ b/server/environment_docker.go @@ -14,6 +14,7 @@ import ( "github.com/docker/go-connections/nat" "github.com/pkg/errors" "github.com/pterodactyl/wings/api" + "github.com/pterodactyl/wings/config" "go.uber.org/zap" "io" "os" @@ -26,13 +27,6 @@ import ( type DockerEnvironment struct { Server *Server - // The user ID that containers should be running as. - User int - - // Defines the configuration for the Docker instance that will allow us to connect - // and create and modify containers. - TimezonePath string - // The Docker client being used for this instance. Client *client.Client @@ -51,22 +45,18 @@ type DockerEnvironment struct { } // Creates a new base Docker environment. A server must still be attached to it. -func NewDockerEnvironment(opts ...func(*DockerEnvironment)) (*DockerEnvironment, error) { +func NewDockerEnvironment(server *Server) error { cli, err := client.NewClientWithOpts(client.FromEnv) if err != nil { - return nil, err + return err } - env := &DockerEnvironment{ - User: 1000, - Client: cli, + server.Environment = &DockerEnvironment{ + Server: server, + Client: cli, } - for _, opt := range opts { - opt(env) - } - - return env, nil + return nil } // Ensure that the Docker environment is always implementing all of the methods @@ -210,11 +200,8 @@ func (d *DockerEnvironment) Start() error { // No reason to try starting a container that is already running. if c.State.Running { d.Server.SetState(ProcessRunningState) - if !d.attached { - return d.Attach() - } - return nil + return d.Attach() } d.Server.SetState(ProcessStartingState) @@ -259,6 +246,8 @@ func (d *DockerEnvironment) Start() error { // No errors, good to continue through. sawError = false + zap.S().Debugw("right before start attach") + return d.Attach() } @@ -354,8 +343,12 @@ func (d *DockerEnvironment) Attach() error { Server: d.Server, } - d.EnableResourcePolling() d.attached = true + go func() { + if err := d.EnableResourcePolling(); err != nil { + zap.S().Warnw("failed to enabled resource polling on server", zap.String("server", d.Server.Uuid), zap.Error(errors.WithStack(err))) + } + }() go func() { defer d.stream.Close() @@ -430,7 +423,10 @@ func (d *DockerEnvironment) EnableResourcePolling() error { var v *types.StatsJSON if err := dec.Decode(&v); err != nil { - zap.S().Warnw("encountered error processing server stats; stopping collection", zap.Error(err)) + if err != io.EOF { + zap.S().Warnw("encountered error processing server stats; stopping collection", zap.Error(err)) + } + d.DisableResourcePolling() return } @@ -537,7 +533,7 @@ func (d *DockerEnvironment) Create() error { conf := &container.Config{ Hostname: "container", - User: strconv.Itoa(d.User), + User: strconv.Itoa(config.Get().System.User.Uid), AttachStdin: true, AttachStdout: true, AttachStderr: true, @@ -569,8 +565,8 @@ func (d *DockerEnvironment) Create() error { ReadOnly: false, }, { - Target: d.TimezonePath, - Source: d.TimezonePath, + Target: config.Get().System.TimezonePath, + Source: config.Get().System.TimezonePath, Type: mount.TypeBind, ReadOnly: true, }, diff --git a/server/server.go b/server/server.go index 6a9e545..84241e8 100644 --- a/server/server.go +++ b/server/server.go @@ -221,23 +221,13 @@ func FromConfiguration(data []byte, cfg *config.SystemConfiguration) (*Server, e s.AddEventListeners() - withConfiguration := func(e *DockerEnvironment) { - e.User = cfg.User.Uid - e.TimezonePath = cfg.TimezonePath - e.Server = s - } - // 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 // some modifications here obviously. - var env Environment - if t, err := NewDockerEnvironment(withConfiguration); err == nil { - env = t - } else { + if err := NewDockerEnvironment(s); err != nil { return nil, err } - s.Environment = env s.Cache = cache.New(time.Minute*10, time.Minute*15) s.Filesystem = Filesystem{ Configuration: cfg, @@ -337,7 +327,7 @@ func (s *Server) SetState(state string) error { } }(s) - zap.S().Debugw("saw server status change event", zap.String("server", s.Uuid), zap.String("status", state)) + zap.S().Debugw("saw server status change event", zap.String("server", s.Uuid), zap.String("status", s.State)) // Emit the event to any listeners that are currently registered. s.Emit(StatusEvent, s.State) diff --git a/wings.go b/wings.go index d956930..59ee8f6 100644 --- a/wings.go +++ b/wings.go @@ -106,48 +106,33 @@ func main() { zap.S().Errorw("failed to create an environment for server", zap.String("server", s.Uuid), zap.Error(err)) } - if r, err := s.Environment.IsRunning(); err != nil { + r, err := s.Environment.IsRunning() + if err != nil { zap.S().Errorw("error checking server environment status", zap.String("server", s.Uuid), zap.Error(err)) - } else if r { - // If the server is currently running on Docker, mark the process as being in that state. - // We never want to stop an instance that is currently running external from Wings since - // that is a good way of keeping things running even if Wings gets in a very corrupted state. - zap.S().Infow("detected server is running, re-attaching to process", zap.String("server", s.Uuid)) - if err := s.Sync(); err != nil { - zap.S().Errorw("failed to sync server state, cannot mark as running", zap.String("server", s.Uuid), zap.Error(errors.WithStack(err))) - } else { - s.SetState(server.ProcessRunningState) - - // If we cannot attach to the environment go ahead and mark the processs as being offline. - if err := s.Environment.Attach(); err != nil { - zap.S().Warnw("error attaching to server environment", zap.String("server", s.Uuid), zap.Error(err)) - s.SetState(server.ProcessOfflineState) - } - } - } else if !r { - // If the server is not in a running state right now but according to the configuration it - // should be, we want to go ahead and restart the instance. - if s.State == server.ProcessRunningState || s.State == server.ProcessStartingState { - zap.S().Infow( - "server state does not match last recorded state in configuration, starting instance now", - zap.String("server", s.Uuid), - ) - - if err := s.Environment.Start(); err != nil { - zap.S().Warnw( - "failed to put server instance back in running state", - zap.String("server", s.Uuid), - zap.Error(errors.WithStack(err)), - ) - } - } else { - if s.State == "" { - // Addresses potentially invalid data in the stored file that can cause Wings to lose - // track of what the actual server state is. - s.SetState(server.ProcessOfflineState) - } - } } + + // If the server is currently running on Docker, mark the process as being in that state. + // We never want to stop an instance that is currently running external from Wings since + // that is a good way of keeping things running even if Wings gets in a very corrupted state. + // + // This will also validate that a server process is running if the last tracked state we have + // is that it was running, but we see that the container process is not currently running. + if r || (!r && (s.State == server.ProcessRunningState || s.State == server.ProcessStartingState)) { + zap.S().Infow("detected server is running, re-attaching to process", zap.String("server", s.Uuid)) + if err := s.Environment.Start(); err != nil { + zap.S().Warnw( + "failed to properly start server detected as already running", + zap.String("server", s.Uuid), + zap.Error(errors.WithStack(err)), + ) + } + + return + } + + // Addresses potentially invalid data in the stored file that can cause Wings to lose + // track of what the actual server state is. + s.SetState(server.ProcessOfflineState) }(serv) }