Improve error handling and reporting for server installation & process boot
This commit is contained in:
parent
33f5cb7df4
commit
0919fb2da6
|
@ -5,6 +5,7 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
"emperror.dev/errors"
|
||||||
"github.com/apex/log"
|
"github.com/apex/log"
|
||||||
"github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types"
|
||||||
"github.com/docker/docker/api/types/network"
|
"github.com/docker/docker/api/types/network"
|
||||||
|
@ -23,7 +24,7 @@ func Docker() (*client.Client, error) {
|
||||||
_conce.Do(func() {
|
_conce.Do(func() {
|
||||||
_client, err = client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation())
|
_client, err = client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation())
|
||||||
})
|
})
|
||||||
return _client, err
|
return _client, errors.Wrap(err, "environment/docker: could not create client")
|
||||||
}
|
}
|
||||||
|
|
||||||
// ConfigureDocker configures the required network for the docker environment.
|
// ConfigureDocker configures the required network for the docker environment.
|
||||||
|
|
|
@ -54,9 +54,10 @@ type Environment struct {
|
||||||
st *system.AtomicString
|
st *system.AtomicString
|
||||||
}
|
}
|
||||||
|
|
||||||
// Creates a new base Docker environment. The ID passed through will be the ID that is used to
|
// New creates a new base Docker environment. The ID passed through will be the
|
||||||
// reference the container from here on out. This should be unique per-server (we use the UUID
|
// ID that is used to reference the container from here on out. This should be
|
||||||
// by default). The container does not need to exist at this point.
|
// unique per-server (we use the UUID by default). The container does not need
|
||||||
|
// to exist at this point.
|
||||||
func New(id string, m *Metadata, c *environment.Configuration) (*Environment, error) {
|
func New(id string, m *Metadata, c *environment.Configuration) (*Environment, error) {
|
||||||
cli, err := environment.Docker()
|
cli, err := environment.Docker()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -45,21 +45,21 @@ func New(ctx context.Context, manager *server.Manager, data []byte) (*Installer,
|
||||||
|
|
||||||
// Unmarshal the environment variables from the request into the server struct.
|
// Unmarshal the environment variables from the request into the server struct.
|
||||||
if b, _, _, err := jsonparser.Get(data, "environment"); err != nil {
|
if b, _, _, err := jsonparser.Get(data, "environment"); err != nil {
|
||||||
return nil, err
|
return nil, errors.WithStackIf(err)
|
||||||
} else {
|
} else {
|
||||||
cfg.EnvVars = make(environment.Variables)
|
cfg.EnvVars = make(environment.Variables)
|
||||||
if err := json.Unmarshal(b, &cfg.EnvVars); err != nil {
|
if err := json.Unmarshal(b, &cfg.EnvVars); err != nil {
|
||||||
return nil, err
|
return nil, errors.WrapIf(err, "installer: could not unmarshal environment variables for server")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unmarshal the allocation mappings from the request into the server struct.
|
// Unmarshal the allocation mappings from the request into the server struct.
|
||||||
if b, _, _, err := jsonparser.Get(data, "allocations", "mappings"); err != nil {
|
if b, _, _, err := jsonparser.Get(data, "allocations", "mappings"); err != nil {
|
||||||
return nil, err
|
return nil, errors.WithStackIf(err)
|
||||||
} else {
|
} else {
|
||||||
cfg.Allocations.Mappings = make(map[string][]int)
|
cfg.Allocations.Mappings = make(map[string][]int)
|
||||||
if err := json.Unmarshal(b, &cfg.Allocations.Mappings); err != nil {
|
if err := json.Unmarshal(b, &cfg.Allocations.Mappings); err != nil {
|
||||||
return nil, err
|
return nil, errors.Wrap(err, "installer: could not unmarshal allocation mappings")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -68,16 +68,18 @@ func New(ctx context.Context, manager *server.Manager, data []byte) (*Installer,
|
||||||
c, err := manager.Client().GetServerConfiguration(ctx, cfg.Uuid)
|
c, err := manager.Client().GetServerConfiguration(ctx, cfg.Uuid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if !remote.IsRequestError(err) {
|
if !remote.IsRequestError(err) {
|
||||||
return nil, err
|
return nil, errors.WithStackIf(err)
|
||||||
}
|
}
|
||||||
return nil, errors.New(err.Error())
|
return nil, errors.WrapIf(err, "installer: could not get server configuration from remote API")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a new server instance using the configuration we wrote to the disk
|
// Create a new server instance using the configuration we wrote to the disk
|
||||||
// so that everything gets instantiated correctly on the struct.
|
// so that everything gets instantiated correctly on the struct.
|
||||||
s, err := manager.InitServer(c)
|
s, err := manager.InitServer(c)
|
||||||
|
if err != nil {
|
||||||
return &Installer{server: s}, err
|
return nil, errors.WrapIf(err, "installer: could not init server instance")
|
||||||
|
}
|
||||||
|
return &Installer{server: s}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Uuid returns the UUID associated with this installer instance.
|
// Uuid returns the UUID associated with this installer instance.
|
||||||
|
|
|
@ -2,6 +2,7 @@ package router
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
@ -38,6 +39,8 @@ func postCreateServer(c *gin.Context) {
|
||||||
buf := bytes.Buffer{}
|
buf := bytes.Buffer{}
|
||||||
buf.ReadFrom(c.Request.Body)
|
buf.ReadFrom(c.Request.Body)
|
||||||
|
|
||||||
|
fmt.Println(buf.String())
|
||||||
|
|
||||||
install, err := installer.New(c.Request.Context(), manager, buf.Bytes())
|
install, err := installer.New(c.Request.Context(), manager, buf.Bytes())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if installer.IsValidationError(err) {
|
if installer.IsValidationError(err) {
|
||||||
|
@ -47,7 +50,7 @@ func postCreateServer(c *gin.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
NewTrackedError(err).Abort(c)
|
// middleware.CaptureAndAbort(c, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -169,7 +169,7 @@ func (m *Manager) ReadStates() (map[string]string, error) {
|
||||||
func (m *Manager) InitServer(data remote.ServerConfigurationResponse) (*Server, error) {
|
func (m *Manager) InitServer(data remote.ServerConfigurationResponse) (*Server, error) {
|
||||||
s, err := New(m.client)
|
s, err := New(m.client)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.WithMessage(err, "loader: failed to instantiate empty server struct")
|
return nil, err
|
||||||
}
|
}
|
||||||
if err := s.UpdateDataStructure(data.Settings); err != nil {
|
if err := s.UpdateDataStructure(data.Settings); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -222,7 +222,7 @@ func (m *Manager) init(ctx context.Context) error {
|
||||||
if !remote.IsRequestError(err) {
|
if !remote.IsRequestError(err) {
|
||||||
return errors.WithStackIf(err)
|
return errors.WithStackIf(err)
|
||||||
}
|
}
|
||||||
return errors.New(err.Error())
|
return errors.WrapIf(err, "manager: failed to retrieve server configurations")
|
||||||
}
|
}
|
||||||
|
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
|
|
|
@ -70,8 +70,8 @@ type Server struct {
|
||||||
wsBagLocker sync.Mutex
|
wsBagLocker sync.Mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns a new server instance with a context and all of the default values set on
|
// New returns a new server instance with a context and all of the default
|
||||||
// the instance.
|
// values set on the struct.
|
||||||
func New(client remote.Client) (*Server, error) {
|
func New(client remote.Client) (*Server, error) {
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
s := Server{
|
s := Server{
|
||||||
|
@ -82,16 +82,16 @@ func New(client remote.Client) (*Server, error) {
|
||||||
transferring: system.NewAtomicBool(false),
|
transferring: system.NewAtomicBool(false),
|
||||||
}
|
}
|
||||||
if err := defaults.Set(&s); err != nil {
|
if err := defaults.Set(&s); err != nil {
|
||||||
return nil, err
|
return nil, errors.Wrap(err, "server: could not set default values for struct")
|
||||||
}
|
}
|
||||||
if err := defaults.Set(&s.cfg); err != nil {
|
if err := defaults.Set(&s.cfg); err != nil {
|
||||||
return nil, err
|
return nil, errors.Wrap(err, "server: could not set defaults for server configuration")
|
||||||
}
|
}
|
||||||
s.resources.State = system.NewAtomicString(environment.ProcessOfflineState)
|
s.resources.State = system.NewAtomicString(environment.ProcessOfflineState)
|
||||||
return &s, nil
|
return &s, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns the UUID for the server instance.
|
// Id returns the UUID for the server instance.
|
||||||
func (s *Server) Id() string {
|
func (s *Server) Id() string {
|
||||||
return s.Config().GetUuid()
|
return s.Config().GetUuid()
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"emperror.dev/errors"
|
"emperror.dev/errors"
|
||||||
"github.com/buger/jsonparser"
|
"github.com/buger/jsonparser"
|
||||||
|
@ -9,24 +10,25 @@ import (
|
||||||
"github.com/pterodactyl/wings/environment"
|
"github.com/pterodactyl/wings/environment"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Merges data passed through in JSON form into the existing server object.
|
// UpdateDataStructure merges data passed through in JSON form into the existing
|
||||||
// Any changes to the build settings will apply immediately in the environment
|
// server object. Any changes to the build settings will apply immediately in
|
||||||
// if the environment supports it.
|
// the environment if the environment supports it.
|
||||||
//
|
//
|
||||||
// The server will be marked as requiring a rebuild on the next boot sequence,
|
// The server will be marked as requiring a rebuild on the next boot sequence,
|
||||||
// it is up to the specific environment to determine what needs to happen when
|
// it is up to the specific environment to determine what needs to happen when
|
||||||
// that is the case.
|
// that is the case.
|
||||||
func (s *Server) UpdateDataStructure(data []byte) error {
|
func (s *Server) UpdateDataStructure(data []byte) error {
|
||||||
src := new(Configuration)
|
src := new(Configuration)
|
||||||
|
fmt.Println("got data", string(data))
|
||||||
if err := json.Unmarshal(data, src); err != nil {
|
if err := json.Unmarshal(data, src); err != nil {
|
||||||
return err
|
return errors.Wrap(err, "server/update: could not unmarshal source data into Configuration struct")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Don't allow obviously corrupted data to pass through into this function. If the UUID
|
// Don't allow obviously corrupted data to pass through into this function. If the UUID
|
||||||
// doesn't match something has gone wrong and the API is attempting to meld this server
|
// doesn't match something has gone wrong and the API is attempting to meld this server
|
||||||
// instance into a totally different one, which would be bad.
|
// instance into a totally different one, which would be bad.
|
||||||
if src.Uuid != "" && s.Id() != "" && src.Uuid != s.Id() {
|
if src.Uuid != "" && s.Id() != "" && src.Uuid != s.Id() {
|
||||||
return errors.New("attempting to merge a data stack with an invalid UUID")
|
return errors.New("server/update: attempting to merge a data stack with an invalid UUID")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Grab a copy of the configuration to work on.
|
// Grab a copy of the configuration to work on.
|
||||||
|
@ -48,7 +50,7 @@ func (s *Server) UpdateDataStructure(data []byte) error {
|
||||||
// Merge the new data object that we have received with the existing server data object
|
// Merge the new data object that we have received with the existing server data object
|
||||||
// and then save it to the disk so it is persistent.
|
// and then save it to the disk so it is persistent.
|
||||||
if err := mergo.Merge(&c, src, mergo.WithOverride); err != nil {
|
if err := mergo.Merge(&c, src, mergo.WithOverride); err != nil {
|
||||||
return err
|
return errors.WithStack(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Don't explode if we're setting CPU limits to 0. Mergo sees that as an empty value
|
// Don't explode if we're setting CPU limits to 0. Mergo sees that as an empty value
|
||||||
|
@ -62,7 +64,7 @@ func (s *Server) UpdateDataStructure(data []byte) error {
|
||||||
// request is going to be boolean. Allegedly.
|
// request is going to be boolean. Allegedly.
|
||||||
if v, err := jsonparser.GetBoolean(data, "container", "oom_disabled"); err != nil {
|
if v, err := jsonparser.GetBoolean(data, "container", "oom_disabled"); err != nil {
|
||||||
if err != jsonparser.KeyPathNotFoundError {
|
if err != jsonparser.KeyPathNotFoundError {
|
||||||
return err
|
return errors.WithStack(err)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
c.Build.OOMDisabled = v
|
c.Build.OOMDisabled = v
|
||||||
|
@ -71,7 +73,7 @@ func (s *Server) UpdateDataStructure(data []byte) error {
|
||||||
// Mergo also cannot handle this boolean value.
|
// Mergo also cannot handle this boolean value.
|
||||||
if v, err := jsonparser.GetBoolean(data, "suspended"); err != nil {
|
if v, err := jsonparser.GetBoolean(data, "suspended"); err != nil {
|
||||||
if err != jsonparser.KeyPathNotFoundError {
|
if err != jsonparser.KeyPathNotFoundError {
|
||||||
return err
|
return errors.WithStack(err)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
c.Suspended = v
|
c.Suspended = v
|
||||||
|
@ -79,7 +81,7 @@ func (s *Server) UpdateDataStructure(data []byte) error {
|
||||||
|
|
||||||
if v, err := jsonparser.GetBoolean(data, "skip_egg_scripts"); err != nil {
|
if v, err := jsonparser.GetBoolean(data, "skip_egg_scripts"); err != nil {
|
||||||
if err != jsonparser.KeyPathNotFoundError {
|
if err != jsonparser.KeyPathNotFoundError {
|
||||||
return err
|
return errors.WithStack(err)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
c.SkipEggScripts = v
|
c.SkipEggScripts = v
|
||||||
|
|
Loading…
Reference in New Issue
Block a user