2017-06-29 10:24:18 +00:00
|
|
|
package control
|
|
|
|
|
2017-07-06 18:51:09 +00:00
|
|
|
import (
|
2017-07-30 18:05:06 +00:00
|
|
|
"errors"
|
|
|
|
|
2018-02-20 20:25:31 +00:00
|
|
|
"github.com/pterodactyl/wings/api/websockets"
|
2017-07-30 18:05:06 +00:00
|
|
|
log "github.com/sirupsen/logrus"
|
2018-02-20 22:38:29 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
type Status string
|
|
|
|
|
|
|
|
const (
|
|
|
|
StatusStopped Status = "stopped"
|
|
|
|
StatusStarting Status = "starting"
|
|
|
|
StatusRunning Status = "running"
|
|
|
|
StatusStopping Status = "stopping"
|
2017-07-06 18:51:09 +00:00
|
|
|
)
|
|
|
|
|
2017-10-01 18:42:17 +00:00
|
|
|
// ErrServerExists is returned when a server already exists on creation.
|
|
|
|
type ErrServerExists struct {
|
|
|
|
id string
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e ErrServerExists) Error() string {
|
|
|
|
return "server " + e.id + " already exists"
|
|
|
|
}
|
|
|
|
|
2017-07-30 18:05:06 +00:00
|
|
|
// Server is a Server
|
|
|
|
type Server interface {
|
|
|
|
Start() error
|
|
|
|
Stop() error
|
2017-10-01 18:42:17 +00:00
|
|
|
Restart() error
|
|
|
|
Kill() error
|
2017-07-30 18:05:06 +00:00
|
|
|
Exec(command string) error
|
|
|
|
Rebuild() error
|
2017-07-30 18:25:42 +00:00
|
|
|
|
2017-10-01 18:42:17 +00:00
|
|
|
Save() error
|
|
|
|
|
|
|
|
Environment() (Environment, error)
|
2018-03-14 09:34:06 +00:00
|
|
|
Websockets() *websockets.Collection
|
2017-10-01 18:42:17 +00:00
|
|
|
|
2017-07-30 18:25:42 +00:00
|
|
|
HasPermission(string, string) bool
|
2017-07-30 18:05:06 +00:00
|
|
|
}
|
|
|
|
|
2017-10-01 18:42:17 +00:00
|
|
|
// ServerStruct is a single instance of a Service managed by the panel
|
2017-08-31 22:01:32 +00:00
|
|
|
type ServerStruct struct {
|
|
|
|
// ID is the unique identifier of the server
|
2018-02-20 22:38:29 +00:00
|
|
|
ID string `json:"uuid" jsonapi:"primary,server"`
|
2017-07-06 18:51:09 +00:00
|
|
|
|
|
|
|
// ServiceName is the name of the service. It is mainly used to allow storing the service
|
|
|
|
// in the config
|
2018-02-20 22:38:29 +00:00
|
|
|
ServiceName string `json:"serviceName"`
|
|
|
|
Service *Service `json:"-" jsonapi:"relation,service"`
|
2017-07-30 18:05:06 +00:00
|
|
|
environment Environment
|
|
|
|
|
|
|
|
// StartupCommand is the command executed in the environment to start the server
|
2018-02-20 22:38:29 +00:00
|
|
|
StartupCommand string `json:"startupCommand" jsonapi:"attr,startup_command"`
|
2017-07-30 18:05:06 +00:00
|
|
|
|
|
|
|
// DockerContainer holds information regarding the docker container when the server
|
|
|
|
// is running in a docker environment
|
2018-02-20 22:38:29 +00:00
|
|
|
DockerContainer dockerContainer `json:"dockerContainer" jsonapi:"attr,docker_container"`
|
2017-07-06 18:51:09 +00:00
|
|
|
|
2017-07-30 18:05:06 +00:00
|
|
|
// EnvironmentVariables are set in the Environment the server is running in
|
2018-02-20 22:38:29 +00:00
|
|
|
EnvironmentVariables map[string]string `json:"environmentVariables" jsonapi:"attr,environment_variables"`
|
2017-07-30 18:05:06 +00:00
|
|
|
|
|
|
|
// Allocations contains the ports and ip addresses assigned to the server
|
2018-02-20 22:38:29 +00:00
|
|
|
Allocations allocations `json:"allocation" jsonapi:"attr,allocations"`
|
2017-07-30 18:05:06 +00:00
|
|
|
|
|
|
|
// Settings are the environment settings and limitations for the server
|
2018-02-20 22:38:29 +00:00
|
|
|
Settings settings `json:"settings" jsonapi:"attr,settings"`
|
2017-07-30 18:05:06 +00:00
|
|
|
|
|
|
|
// Keys are some auth keys we will hopefully replace by something better.
|
2018-02-20 22:38:29 +00:00
|
|
|
// TODO remove
|
2017-07-06 18:51:09 +00:00
|
|
|
Keys map[string][]string `json:"keys"`
|
2018-02-20 22:38:29 +00:00
|
|
|
|
2018-03-14 09:34:06 +00:00
|
|
|
websockets *websockets.Collection
|
2018-02-20 22:38:29 +00:00
|
|
|
status Status
|
2017-07-06 18:51:09 +00:00
|
|
|
}
|
|
|
|
|
2017-07-30 18:05:06 +00:00
|
|
|
type allocations struct {
|
|
|
|
Ports []int16 `json:"ports"`
|
|
|
|
MainIP string `json:"ip"`
|
|
|
|
MainPort int16 `json:"port"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type settings struct {
|
|
|
|
Memory int64 `json:"memory"`
|
|
|
|
Swap int64 `json:"swap"`
|
|
|
|
IO int64 `json:"io"`
|
|
|
|
CPU int16 `json:"cpu"`
|
|
|
|
Disk int64 `json:"disk"`
|
|
|
|
Image string `json:"image"`
|
|
|
|
User string `json:"user"`
|
|
|
|
UserID int16 `json:"userID"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type dockerContainer struct {
|
|
|
|
ID string `json:"id"`
|
|
|
|
Image string `json:"image"`
|
|
|
|
}
|
|
|
|
|
|
|
|
// ensure server implements Server
|
2017-08-31 22:01:32 +00:00
|
|
|
var _ Server = &ServerStruct{}
|
2017-07-30 18:05:06 +00:00
|
|
|
|
2017-08-31 22:01:32 +00:00
|
|
|
type serversMap map[string]*ServerStruct
|
|
|
|
|
|
|
|
var servers = make(serversMap)
|
2017-07-06 18:51:09 +00:00
|
|
|
|
2017-08-31 22:01:32 +00:00
|
|
|
// GetServers returns an array of all servers the daemon manages
|
|
|
|
func GetServers() []Server {
|
|
|
|
serverArray := make([]Server, len(servers))
|
|
|
|
i := 0
|
|
|
|
for _, s := range servers {
|
|
|
|
serverArray[i] = s
|
|
|
|
i++
|
|
|
|
}
|
|
|
|
return serverArray
|
|
|
|
}
|
|
|
|
|
2017-07-06 18:51:09 +00:00
|
|
|
// GetServer returns the server identified by the provided uuid
|
2017-08-31 22:01:32 +00:00
|
|
|
func GetServer(id string) Server {
|
|
|
|
server := servers[id]
|
2017-08-02 19:35:15 +00:00
|
|
|
if server == nil {
|
|
|
|
return nil // https://golang.org/doc/faq#nil_error
|
|
|
|
}
|
|
|
|
return server
|
2017-07-06 18:51:09 +00:00
|
|
|
}
|
|
|
|
|
2017-08-31 22:01:32 +00:00
|
|
|
// CreateServer creates a new server
|
|
|
|
func CreateServer(server *ServerStruct) (Server, error) {
|
2017-10-01 18:42:17 +00:00
|
|
|
if servers[server.ID] != nil {
|
|
|
|
return nil, ErrServerExists{server.ID}
|
|
|
|
}
|
2017-08-31 22:01:32 +00:00
|
|
|
servers[server.ID] = server
|
2017-10-01 18:42:17 +00:00
|
|
|
if err := server.Save(); err != nil {
|
2018-02-20 22:38:29 +00:00
|
|
|
delete(servers, server.ID)
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if err := server.init(); err != nil {
|
|
|
|
DeleteServer(server.ID)
|
2017-10-01 18:42:17 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
2017-08-31 22:01:32 +00:00
|
|
|
return server, nil
|
2017-07-30 18:05:06 +00:00
|
|
|
}
|
|
|
|
|
2017-08-31 22:01:32 +00:00
|
|
|
// DeleteServer deletes a server and all related files
|
|
|
|
// NOTE: This is not reversible.
|
2017-10-01 18:42:17 +00:00
|
|
|
func DeleteServer(id string) error {
|
|
|
|
if err := deleteServerFolder(id); err != nil {
|
|
|
|
log.WithField("server", id).WithError(err).Error("Failed to delete server.")
|
|
|
|
}
|
|
|
|
delete(servers, id)
|
2017-08-31 22:01:32 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2018-02-20 22:38:29 +00:00
|
|
|
func (s *ServerStruct) init() error {
|
|
|
|
// TODO: Properly use the correct service, mock for now.
|
|
|
|
s.Service = &Service{
|
|
|
|
DockerImage: "quay.io/pterodactyl/core:java",
|
|
|
|
EnvironmentName: "docker",
|
|
|
|
}
|
|
|
|
s.status = StatusStopped
|
|
|
|
|
2018-03-14 09:34:06 +00:00
|
|
|
s.websockets = websockets.NewCollection()
|
2018-02-20 22:38:29 +00:00
|
|
|
go s.websockets.Run()
|
|
|
|
|
|
|
|
var err error
|
|
|
|
if s.environment == nil {
|
|
|
|
switch s.GetService().EnvironmentName {
|
|
|
|
case "docker":
|
|
|
|
s.environment, err = NewDockerEnvironment(s)
|
|
|
|
default:
|
|
|
|
log.WithField("service", s.ServiceName).Error("Invalid environment name")
|
|
|
|
return errors.New("Invalid environment name")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2017-08-31 22:01:32 +00:00
|
|
|
func (s *ServerStruct) Start() error {
|
2018-02-20 22:38:29 +00:00
|
|
|
s.SetStatus(StatusStarting)
|
2017-10-01 18:42:17 +00:00
|
|
|
env, err := s.Environment()
|
|
|
|
if err != nil {
|
2018-02-20 22:38:29 +00:00
|
|
|
s.SetStatus(StatusStopped)
|
2017-07-30 18:05:06 +00:00
|
|
|
return err
|
|
|
|
}
|
2017-10-01 18:42:17 +00:00
|
|
|
if !env.Exists() {
|
|
|
|
if err := env.Create(); err != nil {
|
2018-02-20 22:38:29 +00:00
|
|
|
s.SetStatus(StatusStopped)
|
2017-10-01 18:42:17 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return env.Start()
|
2017-07-30 18:05:06 +00:00
|
|
|
}
|
|
|
|
|
2017-08-31 22:01:32 +00:00
|
|
|
func (s *ServerStruct) Stop() error {
|
2018-02-20 22:38:29 +00:00
|
|
|
s.SetStatus(StatusStopping)
|
2017-10-01 18:42:17 +00:00
|
|
|
env, err := s.Environment()
|
|
|
|
if err != nil {
|
2018-02-20 22:38:29 +00:00
|
|
|
s.SetStatus(StatusRunning)
|
2017-07-30 18:05:06 +00:00
|
|
|
return err
|
2017-10-01 18:42:17 +00:00
|
|
|
}
|
|
|
|
return env.Stop()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *ServerStruct) Restart() error {
|
|
|
|
if err := s.Stop(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return s.Start()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *ServerStruct) Kill() error {
|
|
|
|
env, err := s.Environment()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return env.Kill()
|
2017-07-30 18:05:06 +00:00
|
|
|
}
|
|
|
|
|
2017-08-31 22:01:32 +00:00
|
|
|
func (s *ServerStruct) Exec(command string) error {
|
2017-10-01 18:42:17 +00:00
|
|
|
env, err := s.Environment()
|
|
|
|
if err != nil {
|
2017-07-30 18:05:06 +00:00
|
|
|
return err
|
2017-10-01 18:42:17 +00:00
|
|
|
}
|
|
|
|
return env.Exec(command)
|
2017-07-30 18:05:06 +00:00
|
|
|
}
|
|
|
|
|
2017-08-31 22:01:32 +00:00
|
|
|
func (s *ServerStruct) Rebuild() error {
|
2017-10-01 18:42:17 +00:00
|
|
|
env, err := s.Environment()
|
|
|
|
if err != nil {
|
2017-07-30 18:05:06 +00:00
|
|
|
return err
|
2017-10-01 18:42:17 +00:00
|
|
|
}
|
|
|
|
return env.ReCreate()
|
2017-07-30 18:05:06 +00:00
|
|
|
}
|