diff --git a/go.mod b/go.mod index 4893707..3d6912f 100644 --- a/go.mod +++ b/go.mod @@ -6,6 +6,7 @@ require ( github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 // indirect github.com/Microsoft/go-winio v0.4.7 // indirect github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 // indirect + github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23 github.com/containerd/fifo v0.0.0-20190226154929-a9fb20d87448 // indirect github.com/docker/distribution v2.7.1+incompatible // indirect diff --git a/go.sum b/go.sum index 817accd..369b4bf 100644 --- a/go.sum +++ b/go.sum @@ -4,6 +4,8 @@ github.com/Microsoft/go-winio v0.4.7 h1:vOvDiY/F1avSWlCWiKJjdYKz2jVjTK3pWPHndeG4 github.com/Microsoft/go-winio v0.4.7/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA= github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw= github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk= +github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a h1:idn718Q4B6AGu/h5Sxe66HYVdqdGu2l9Iebqhi/AEoA= +github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23 h1:D21IyuvjDCshj1/qq+pCNd3VZOAEI9jy6Bi131YlXgI= github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= github.com/containerd/fifo v0.0.0-20190226154929-a9fb20d87448 h1:PUD50EuOMkXVcpBIA/R95d56duJR9VxhwncsFbNnxW4= diff --git a/http.go b/http.go index bfbcdf6..2c95880 100644 --- a/http.go +++ b/http.go @@ -8,6 +8,7 @@ import ( "github.com/buger/jsonparser" "github.com/gorilla/websocket" "github.com/julienschmidt/httprouter" + "github.com/pterodactyl/wings/installer" "github.com/pterodactyl/wings/server" "go.uber.org/zap" "io" @@ -391,6 +392,20 @@ func (rt *Router) routeServerSendCommand(w http.ResponseWriter, r *http.Request, w.WriteHeader(http.StatusNoContent) } +func (rt *Router) routeCreateServer(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { + defer r.Body.Close() + + err, inst := installer.New(rt.ReaderToBytes(r.Body)) + + if err != nil { + zap.S().Warnw("failed to validate the received data", zap.Error(err)) + } + + inst.Execute() + + w.WriteHeader(http.StatusAccepted) +} + func (rt *Router) ReaderToBytes(r io.Reader) []byte { buf := bytes.Buffer{} buf.ReadFrom(r) @@ -410,6 +425,7 @@ func (rt *Router) ConfigureRouter() *httprouter.Router { router.GET("/api/servers/:server/files/contents", rt.AuthenticateRequest(rt.routeServerFileRead)) router.GET("/api/servers/:server/files/list-directory", rt.AuthenticateRequest(rt.routeServerListDirectory)) router.PUT("/api/servers/:server/files/rename", rt.AuthenticateRequest(rt.routeServerRenameFile)) + router.POST("/api/servers", rt.AuthenticateToken(rt.routeCreateServer)); router.POST("/api/servers/:server/files/copy", rt.AuthenticateRequest(rt.routeServerCopyFile)) router.POST("/api/servers/:server/files/write", rt.AuthenticateRequest(rt.routeServerWriteFile)) router.POST("/api/servers/:server/files/create-directory", rt.AuthenticateRequest(rt.routeServerCreateDirectory)) diff --git a/installer/installer.go b/installer/installer.go new file mode 100644 index 0000000..7d9f8fd --- /dev/null +++ b/installer/installer.go @@ -0,0 +1,99 @@ +package installer + +import ( + "encoding/json" + "errors" + "github.com/asaskevich/govalidator" + "github.com/buger/jsonparser" + "github.com/pterodactyl/wings/server" + "go.uber.org/zap" +) + +type Installer struct { + server *server.Server +} + +// Validates the received data to ensure that all of the required fields +// have been passed along in the request. This should be manually run before +// calling Execute(). +func New(data []byte) (error, *Installer) { + if !govalidator.IsUUIDv4(getString(data, "uuid")) { + return errors.New("uuid provided was not in a valid format"), nil + } + + if !govalidator.IsUUIDv4(getString(data, "service", "egg")) { + return errors.New("service egg provided was not in a valid format"), nil + } + + s := &server.Server{ + Uuid: getString(data, "uuid"), + Suspended: false, + State: server.ProcessOfflineState, + Invocation: "", + EnvVars: make(map[string]string), + Build: &server.BuildSettings{ + MemoryLimit: getInt(data, "build", "memory"), + Swap: getInt(data, "build", "swap"), + IoWeight: uint16(getInt(data, "build", "io")), + CpuLimit: getInt(data, "build", "cpu"), + DiskSpace: getInt(data, "build", "disk"), + }, + Allocations: &server.Allocations{ + Mappings: make(map[string][]int), + }, + } + + s.Allocations.DefaultMapping.Ip = getString(data, "allocations", "default", "ip") + s.Allocations.DefaultMapping.Port = int(getInt(data, "allocations", "default", "port")) + + jsonparser.ObjectEach(data, func(key []byte, value []byte, dataType jsonparser.ValueType, offset int) error { + s.EnvVars[string(key)] = string(value) + + return nil + }, "environment") + + jsonparser.ArrayEach(data, func(value []byte, dataType jsonparser.ValueType, offset int, err error) { + var dat map[string][]int + if err := json.Unmarshal(value, &dat); err != nil { + return + } + + for i, k := range dat { + s.Allocations.Mappings[i] = k + } + }, "allocations", "mappings") + + s.Container.Image = getString(data, "container", "image") + + return nil, &Installer{ + server: s, + } +} + +// Returns the UUID associated with this installer instance. +func (i *Installer) Uuid() string { + return i.server.Uuid +} + +// Executes the installer process, creating the server and running through the +// associated installation process based on the parameters passed through for +// the server instance. +func (i *Installer) Execute() error { + zap.S().Debugw("beginning installation process for server", zap.String("server", i.server.Uuid)) + + return nil +} + +// Returns a string value from the JSON data provided. +func getString(data []byte, key ...string) string { + value, _ := jsonparser.GetString(data, key...) + + return value +} + +// Returns an int value from the JSON data provided. +func getInt(data []byte, key ...string) int64 { + value, _ := jsonparser.GetInt(data, key...) + + return value +}