diff --git a/remote/http.go b/remote/http.go index 7063923..7812799 100644 --- a/remote/http.go +++ b/remote/http.go @@ -29,7 +29,7 @@ type Client interface { SetArchiveStatus(ctx context.Context, uuid string, successful bool) error SetBackupStatus(ctx context.Context, backup string, data BackupRequest) error SendRestorationStatus(ctx context.Context, backup string, successful bool) error - SetInstallationStatus(ctx context.Context, uuid string, successful bool) error + SetInstallationStatus(ctx context.Context, uuid string, data InstallStatusRequest) error SetTransferStatus(ctx context.Context, uuid string, successful bool) error ValidateSftpCredentials(ctx context.Context, request SftpAuthRequest) (SftpAuthResponse, error) SendActivityLogs(ctx context.Context, activity []models.Activity) error diff --git a/remote/servers.go b/remote/servers.go index 0b7aca2..68ba532 100644 --- a/remote/servers.go +++ b/remote/servers.go @@ -19,7 +19,7 @@ const ( ProcessStopNativeStop = "stop" ) -// GetServers returns all of the servers that are present on the Panel making +// GetServers returns all the servers that are present on the Panel making // parallel API calls to the endpoint if more than one page of servers is // returned. func (c *client) GetServers(ctx context.Context, limit int) ([]RawServerData, error) { @@ -58,7 +58,7 @@ func (c *client) GetServers(ctx context.Context, limit int) ([]RawServerData, er // // This handles Wings exiting during either of these processes which will leave // things in a bad state within the Panel. This API call is executed once Wings -// has fully booted all of the servers. +// has fully booted all the servers. func (c *client) ResetServersState(ctx context.Context) error { res, err := c.Post(ctx, "/servers/reset", nil) if err != nil { @@ -92,8 +92,8 @@ func (c *client) GetInstallationScript(ctx context.Context, uuid string) (Instal return config, err } -func (c *client) SetInstallationStatus(ctx context.Context, uuid string, successful bool) error { - resp, err := c.Post(ctx, fmt.Sprintf("/servers/%s/install", uuid), d{"successful": successful}) +func (c *client) SetInstallationStatus(ctx context.Context, uuid string, data InstallStatusRequest) error { + resp, err := c.Post(ctx, fmt.Sprintf("/servers/%s/install", uuid), data) if err != nil { return err } @@ -127,7 +127,7 @@ func (c *client) SetTransferStatus(ctx context.Context, uuid string, successful // password combination provided is associated with a valid server on the instance // using the Panel's authentication control mechanisms. This will get itself // throttled if too many requests are made, allowing us to completely offload -// all of the authorization security logic to the Panel. +// all the authorization security logic to the Panel. func (c *client) ValidateSftpCredentials(ctx context.Context, request SftpAuthRequest) (SftpAuthResponse, error) { var auth SftpAuthResponse res, err := c.Post(ctx, "/sftp/auth", request) diff --git a/remote/types.go b/remote/types.go index afbb974..f636730 100644 --- a/remote/types.go +++ b/remote/types.go @@ -92,8 +92,8 @@ type SftpAuthResponse struct { } type OutputLineMatcher struct { - // The raw string to match against. This may or may not be prefixed with - // regex: which indicates we want to match against the regex expression. + // raw string to match against. This may or may not be prefixed with + // `regex:` which indicates we want to match against the regex expression. raw []byte reg *regexp.Regexp } @@ -139,9 +139,9 @@ type ProcessStopConfiguration struct { } // ProcessConfiguration defines the process configuration for a given server -// instance. This sets what Wings is looking for to mark a server as done starting -// what to do when stopping, and what changes to make to the configuration file -// for a server. +// instance. This sets what Wings is looking for to mark a server as done +// starting what to do when stopping, and what changes to make to the +// configuration file for a server. type ProcessConfiguration struct { Startup struct { Done []*OutputLineMatcher `json:"done"` @@ -169,3 +169,8 @@ type BackupRequest struct { Successful bool `json:"successful"` Parts []BackupPart `json:"parts"` } + +type InstallStatusRequest struct { + Successful bool `json:"successful"` + Reinstall bool `json:"reinstall"` +} diff --git a/router/router_server.go b/router/router_server.go index 2c41272..df721cf 100644 --- a/router/router_server.go +++ b/router/router_server.go @@ -153,9 +153,15 @@ func postServerSync(c *gin.Context) { func postServerInstall(c *gin.Context) { s := ExtractServer(c) - go func(serv *server.Server) { - if err := serv.Install(true); err != nil { - serv.Log().WithField("error", err).Error("failed to execute server installation process") + go func(s *server.Server) { + s.Log().Info("syncing server state with remote source before executing installation process") + if err := s.Sync(); err != nil { + s.Log().WithField("error", err).Error("failed to sync server state with Panel") + return + } + + if err := s.Install(); err != nil { + s.Log().WithField("error", err).Error("failed to execute server installation process") } }(s) diff --git a/router/router_system.go b/router/router_system.go index 48bd20f..0ab6585 100644 --- a/router/router_system.go +++ b/router/router_system.go @@ -75,7 +75,7 @@ func postCreateServer(c *gin.Context) { return } - if err := i.Server().Install(false); err != nil { + if err := i.Server().Install(); err != nil { log.WithFields(log.Fields{"server": i.Server().ID(), "error": err}).Error("failed to run install process for server") return } diff --git a/server/install.go b/server/install.go index 16d8c1e..6fc5174 100644 --- a/server/install.go +++ b/server/install.go @@ -32,19 +32,17 @@ import ( // // Pass true as the first argument in order to execute a server sync before the // process to ensure the latest information is used. -func (s *Server) Install(sync bool) error { - if sync { - s.Log().Info("syncing server state with remote source before executing installation process") - if err := s.Sync(); err != nil { - return errors.WrapIf(err, "install: failed to sync server state with Panel") - } - } +func (s *Server) Install() error { + return s.install(false) +} +func (s *Server) install(reinstall bool) error { var err error if !s.Config().SkipEggScripts { - // Send the start event so the Panel can automatically update. We don't send this unless the process - // is actually going to run, otherwise all sorts of weird rapid UI behavior happens since there isn't - // an actual install process being executed. + // Send the start event so the Panel can automatically update. We don't + // send this unless the process is actually going to run, otherwise all + // sorts of weird rapid UI behavior happens since there isn't an actual + // install process being executed. s.Events().Publish(InstallStartedEvent, "") err = s.internalInstall() @@ -53,12 +51,13 @@ func (s *Server) Install(sync bool) error { } s.Log().WithField("was_successful", err == nil).Debug("notifying panel of server install state") - if serr := s.SyncInstallState(err == nil); serr != nil { + if serr := s.SyncInstallState(err == nil, reinstall); serr != nil { 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 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", err) } @@ -66,19 +65,20 @@ func (s *Server) Install(sync bool) error { l.Warn("failed to notify panel of server install state") } - // Ensure that the server is marked as offline at this point, otherwise you end up - // with a blank value which is a bit confusing. + // Ensure that the server is marked as offline at this point, otherwise you + // end up with a blank value which is a bit confusing. s.Environment.SetState(environment.ProcessOfflineState) - // Push an event to the websocket so we can auto-refresh the information in the panel once - // the install is completed. + // Push an event to the websocket, so we can auto-refresh the information in + // the panel once the installation is completed. s.Events().Publish(InstallCompletedEvent, "") return errors.WithStackIf(err) } -// Reinstalls a server's software by utilizing the install script for the server egg. This -// does not touch any existing files for the server, other than what the script modifies. +// Reinstall reinstalls a server's software by utilizing the installation script +// for the server egg. This does not touch any existing files for the server, +// other than what the script modifies. func (s *Server) Reinstall() error { if s.Environment.State() != environment.ProcessOfflineState { s.Log().Debug("waiting for server instance to enter a stopped state") @@ -87,7 +87,12 @@ func (s *Server) Reinstall() error { } } - return s.Install(true) + s.Log().Info("syncing server state with remote source before executing re-installation process") + if err := s.Sync(); err != nil { + return errors.WrapIf(err, "install: failed to sync server state with Panel") + } + + return s.install(true) } // Internal installation function used to simplify reporting back to the Panel. @@ -116,8 +121,9 @@ type InstallationProcess struct { client *client.Client } -// Generates a new installation process struct that will be used to create containers, -// and otherwise perform installation commands for a server. +// NewInstallationProcess returns a new installation process struct that will be +// used to create containers and otherwise perform installation commands for a +// server. func NewInstallationProcess(s *Server, script *remote.InstallationScript) (*InstallationProcess, error) { proc := &InstallationProcess{ Script: script, @@ -133,8 +139,8 @@ func NewInstallationProcess(s *Server, script *remote.InstallationScript) (*Inst return proc, nil } -// Determines if the server is actively running the installation process by checking the status -// of the installer lock. +// IsInstalling returns if the server is actively running the installation +// process by checking the status of the installer lock. func (s *Server) IsInstalling() bool { return s.installing.Load() } @@ -155,7 +161,7 @@ func (s *Server) SetRestoring(state bool) { s.restoring.Store(state) } -// Removes the installer container for the server. +// RemoveContainer removes the installation container for the server. func (ip *InstallationProcess) RemoveContainer() error { err := ip.client.ContainerRemove(ip.Server.Context(), ip.Server.ID()+"_installer", types.ContainerRemoveOptions{ RemoveVolumes: true, @@ -328,14 +334,14 @@ func (ip *InstallationProcess) BeforeExecute() error { return nil } -// Returns the log path for the installation process. +// GetLogPath returns the log path for the installation process. func (ip *InstallationProcess) GetLogPath() string { 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 -// process to store in the server configuration directory, and then destroys the associated -// installation container. +// AfterExecute cleans up after the execution of the installation process. +// This grabs the logs from the process to store in the server configuration +// directory, and then destroys the associated installation container. func (ip *InstallationProcess) AfterExecute(containerId string) error { defer ip.RemoveContainer() @@ -525,7 +531,7 @@ func (ip *InstallationProcess) StreamOutput(ctx context.Context, id string) erro return nil } -// resourceLimits returns the install container specific resource limits. This +// resourceLimits returns resource limits for the installation container. This // looks at the globally defined install container limits and attempts to use // the higher of the two (defined limits & server limits). This allows for servers // with super low limits (e.g. Discord bots with 128Mb of memory) to perform more @@ -537,8 +543,8 @@ func (ip *InstallationProcess) StreamOutput(ctx context.Context, id string) erro func (ip *InstallationProcess) resourceLimits() container.Resources { limits := config.Get().Docker.InstallerLimits - // Create a copy of the configuration so we're not accidentally making changes - // to the underlying server build data. + // Create a copy of the configuration, so we're not accidentally making + // changes to the underlying server build data. c := *ip.Server.Config() cfg := c.Build if cfg.MemoryLimit < limits.Memory { @@ -562,10 +568,12 @@ func (ip *InstallationProcess) resourceLimits() container.Resources { return resources } -// SyncInstallState makes a HTTP request to the Panel instance notifying it that +// SyncInstallState makes an HTTP request to the Panel instance notifying it that // the server has completed the installation process, and what the state of the -// 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. -func (s *Server) SyncInstallState(successful bool) error { - return s.client.SetInstallationStatus(s.Context(), s.ID(), successful) +// server is. +func (s *Server) SyncInstallState(successful, reinstall bool) error { + return s.client.SetInstallationStatus(s.Context(), s.ID(), remote.InstallStatusRequest{ + Successful: successful, + Reinstall: reinstall, + }) }