diff --git a/cmd/root.go b/cmd/root.go index 6ce53c7..14afe6a 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -21,6 +21,7 @@ import ( var configPath = "config.yml" var debug = false +var shouldRunProfiler = false var root = &cobra.Command{ Use: "wings", @@ -32,12 +33,16 @@ var root = &cobra.Command{ func init() { root.PersistentFlags().StringVar(&configPath, "config", "config.yml", "set the location for the configuration file") root.PersistentFlags().BoolVar(&debug, "debug", false, "pass in order to run wings in debug mode") + root.PersistentFlags().BoolVar(&shouldRunProfiler, "profile", false, "pass in order to profile wings") root.AddCommand(configureCmd) } -func rootCmdRun(cmd *cobra.Command, args []string) { - defer profile.Start().Stop() +func rootCmdRun(*cobra.Command, []string) { + // Profile wings in production!!!! + if shouldRunProfiler { + defer profile.Start().Stop() + } c, err := config.ReadConfiguration(configPath) if err != nil { @@ -113,7 +118,7 @@ func rootCmdRun(cmd *cobra.Command, args []string) { // Create a server environment if none exists currently. This allows us to recover from Docker // being reinstalled on the host system for example. - zap.S().Infow("ensuring envrionment exists", zap.String("server", s.Uuid)) + zap.S().Infow("ensuring environment exists", zap.String("server", s.Uuid)) if err := s.Environment.Create(); err != nil { zap.S().Errorw("failed to create an environment for server", zap.String("server", s.Uuid), zap.Error(err)) } @@ -161,6 +166,11 @@ func rootCmdRun(cmd *cobra.Command, args []string) { zap.S().Errorw("failed to create archive directory", zap.Error(err)) } + // Ensure the backup directory exists. + if err := os.MkdirAll(c.System.BackupDirectory, 0755); err != nil { + zap.S().Errorw("failed to create backup directory", zap.Error(err)) + } + zap.S().Infow("configuring webserver", zap.Bool("ssl", c.Api.Ssl.Enabled), zap.String("host", c.Api.Host), zap.Int("port", c.Api.Port)) r := router.Configure() diff --git a/data/.gitignore b/data/.gitignore index 9c677ba..3da70bf 100644 --- a/data/.gitignore +++ b/data/.gitignore @@ -1,3 +1,4 @@ servers/*.yml !install_logs/.gitkeep -install_logs/* \ No newline at end of file +install_logs/* +states.json diff --git a/installer/installer.go b/installer/installer.go index bfd5f9b..2f8ef55 100644 --- a/installer/installer.go +++ b/installer/installer.go @@ -5,6 +5,7 @@ import ( "github.com/asaskevich/govalidator" "github.com/buger/jsonparser" "github.com/pkg/errors" + "github.com/pterodactyl/wings/api" "github.com/pterodactyl/wings/config" "github.com/pterodactyl/wings/server" "go.uber.org/zap" @@ -74,17 +75,26 @@ func New(data []byte) (*Installer, error) { s.Container.Image = getString(data, "container", "image") - b, err := s.WriteConfigurationToDisk() + c, rerr, err := api.NewRequester().GetServerConfiguration(s.Uuid) + if err != nil || rerr != nil { + if err != nil { + return nil, errors.WithStack(err) + } + + return nil, errors.New(rerr.String()) + } + + /*b, err := s.WriteConfigurationToDisk() if err != nil { return nil, err - } + }*/ // Destroy the temporary server instance. s = nil // Create a new server instance using the configuration we wrote to the disk // so that everything gets instantiated correctly on the struct. - s2, err := server.FromConfiguration(b, &config.Get().System, nil) + s2, err := server.FromConfiguration(c, &config.Get().System) return &Installer{ server: s2, diff --git a/server/server.go b/server/server.go index aa71faa..bf9508d 100644 --- a/server/server.go +++ b/server/server.go @@ -1,6 +1,7 @@ package server import ( + "encoding/json" "fmt" "github.com/creasty/defaults" "github.com/patrickmn/go-cache" @@ -9,10 +10,7 @@ import ( "github.com/pterodactyl/wings/config" "github.com/remeh/sizedwaitgroup" "go.uber.org/zap" - "gopkg.in/yaml.v2" - "io/ioutil" "os" - "path" "strings" "sync" "time" @@ -160,11 +158,6 @@ func LoadDirectory(dir string, cfg *config.SystemConfiguration) error { // the road to help big instances scale better. wg := sizedwaitgroup.New(10) - f, err := ioutil.ReadDir(dir) - if err != nil { - return err - } - configs, rerr, err := api.NewRequester().GetAllServerConfigurations() if err != nil || rerr != nil { if err != nil { @@ -174,39 +167,32 @@ func LoadDirectory(dir string, cfg *config.SystemConfiguration) error { return errors.New(rerr.String()) } + states, err := FetchServerStates() + if err != nil { + return errors.WithStack(err) + } + servers = NewCollection(nil) - for _, file := range f { - if !strings.HasSuffix(file.Name(), ".yml") || file.IsDir() { - continue - } - + for uuid, data := range configs { wg.Add() - // For each of the YAML files we find, parse it and create a new server - // configuration object that can then be returned to the caller. - go func(file os.FileInfo) { + go func(uuid string, data *api.ServerConfigurationResponse) { defer wg.Done() - b, err := ioutil.ReadFile(path.Join(dir, file.Name())) + s, err := FromConfiguration(data, cfg) if err != nil { - zap.S().Errorw("failed to read server configuration file, skipping...", zap.String("server", file.Name()), zap.Error(err)) + zap.S().Errorw("failed to load server, skipping...", zap.String("server", uuid), zap.Error(err)) return } - s, err := FromConfiguration(b, cfg, configs[file.Name()[:len(file.Name())-4]]) - if err != nil { - if IsServerDoesNotExistError(err) { - zap.S().Infow("server does not exist on remote system", zap.String("server", file.Name())) - } else { - zap.S().Errorw("failed to parse server configuration, skipping...", zap.String("server", file.Name()), zap.Error(err)) - } - - return + if state, exists := states[s.Uuid]; exists { + s.State = state + zap.S().Debug("loaded server state from cache", zap.String("server", s.Uuid), zap.String("state", s.State)) } servers.Add(s) - }(file) + }(uuid, data) } // Wait until we've processed all of the configuration files in the directory @@ -219,7 +205,7 @@ func LoadDirectory(dir string, cfg *config.SystemConfiguration) error { // Initializes a server using a data byte array. This will be marshaled into the // given struct using a YAML marshaler. This will also configure the given environment // for a server. -func FromConfiguration(data []byte, cfg *config.SystemConfiguration, apiCfg *api.ServerConfigurationResponse) (*Server, error) { +func FromConfiguration(data *api.ServerConfigurationResponse, cfg *config.SystemConfiguration) (*Server, error) { s := new(Server) if err := defaults.Set(s); err != nil { @@ -228,7 +214,12 @@ func FromConfiguration(data []byte, cfg *config.SystemConfiguration, apiCfg *api s.Init() - if err := yaml.Unmarshal(data, s); err != nil { + j, err := json.Marshal(data) + if err != nil { + return nil, err + } + + if err := s.UpdateDataStructure(j, false); err != nil { return nil, err } @@ -252,16 +243,9 @@ func FromConfiguration(data []byte, cfg *config.SystemConfiguration, apiCfg *api s.Resources = ResourceUsage{} // Forces the configuration to be synced with the panel. - if apiCfg == nil { - zap.S().Debug("syncing config with panel", zap.String("server", s.Uuid)) - if err := s.Sync(); err != nil { - return nil, err - } - } else { - zap.S().Debug("syncing with local config", zap.String("server", s.Uuid)) - if err := s.SyncWithConfiguration(apiCfg); err != nil { - return nil, err - } + zap.S().Debug("syncing config with panel", zap.String("server", s.Uuid)) + if err := s.SyncWithConfiguration(data); err != nil { + return nil, err } return s, nil @@ -371,11 +355,15 @@ func (s *Server) SetState(state string) error { // // We also get the benefit of server status changes always propagating corrected configurations // to the disk should we forget to do it elsewhere. - go func(server *Server) { - if _, err := server.WriteConfigurationToDisk(); err != nil { + go func() { + /*if _, err := server.WriteConfigurationToDisk(); err != nil { zap.S().Warnw("failed to write server state change to disk", zap.String("server", server.Uuid), zap.Error(err)) + }*/ + + if err := SaveServerStates(); err != nil { + zap.S().Warnw("failed to write server states to disk", zap.Error(err)) } - }(s) + }() zap.S().Debugw("saw server status change event", zap.String("server", s.Uuid), zap.String("status", s.State)) diff --git a/server/state.go b/server/state.go new file mode 100644 index 0000000..0528449 --- /dev/null +++ b/server/state.go @@ -0,0 +1,95 @@ +package server + +import ( + "encoding/json" + "github.com/pkg/errors" + "os" +) + +var statesFile = "data/states.json" + +// DoesStatesFileExist . +func DoesStatesFileExist() (bool, error) { + if _, err := os.Stat(statesFile); err != nil { + if !os.IsNotExist(err) { + return false, errors.WithStack(err) + } + + return false, nil + } + + return true, nil +} + +// FetchServerStates . +func FetchServerStates() (map[string]string, error) { + // Check if the states file exists. + exists, err := DoesStatesFileExist() + if err != nil { + return nil, errors.WithStack(err) + } + + // Return an empty map if the file does not exist. + if !exists { + return map[string]string{}, nil + } + + // Open the states file. + f, err := os.Open(statesFile) + if err != nil { + return nil, errors.WithStack(err) + } + defer f.Close() + + // Convert the json object to a map. + states := map[string]string{} + if err := json.NewDecoder(f).Decode(states); err != nil { + return nil, errors.WithStack(err) + } + + return states, nil +} + +// SaveServerStates . +func SaveServerStates() error { + // Get the states of all servers on the daemon. + states := map[string]string{} + for _, s := range GetServers().All() { + states[s.Uuid] = s.State + } + + // Convert the map to a json object. + data, err := json.Marshal(states) + if err != nil { + return errors.WithStack(err) + } + + // Check if the states file exists. + exists, err := DoesStatesFileExist() + if err != nil { + return errors.WithStack(err) + } + + // Create the file if it doesn't exist or open it if it already does. + var f *os.File + if !exists { + f, err = os.Create(statesFile) + if err != nil { + return errors.WithStack(err) + } + } else { + f, err = os.Open(statesFile) + if err != nil { + return errors.WithStack(err) + } + } + defer f.Close() + + // Write the data to the file + if _, err := f.Write(data); err != nil { + return errors.WithStack(err) + } + + // Save the file basically. + return f.Sync() +} diff --git a/server/update.go b/server/update.go index 9e74676..d2b36ae 100644 --- a/server/update.go +++ b/server/update.go @@ -69,9 +69,9 @@ func (s *Server) UpdateDataStructure(data []byte, background bool) error { s.Allocations.Mappings = src.Allocations.Mappings } - if _, err := s.WriteConfigurationToDisk(); err != nil { + /*if _, err := s.WriteConfigurationToDisk(); err != nil { return errors.WithStack(err) - } + }*/ if background { s.runBackgroundActions()