current state of api & docker env implementation
This commit is contained in:
		
							parent
							
								
									d1fdc713f5
								
							
						
					
					
						commit
						c979285eb9
					
				|  | @ -29,6 +29,15 @@ func (api *API) Listen() { | ||||||
| 
 | 
 | ||||||
| 	api.router = gin.Default() | 	api.router = gin.Default() | ||||||
| 
 | 
 | ||||||
|  | 	api.router.Use(func(c *gin.Context) { | ||||||
|  | 		c.Header("Access-Control-Allow-Origin", "*") | ||||||
|  | 	}) | ||||||
|  | 
 | ||||||
|  | 	api.router.OPTIONS("/", func(c *gin.Context) { | ||||||
|  | 		c.Header("Access-Control-Allow-Methods", "POST, GET, PUT, DELETE, OPTIONS") | ||||||
|  | 		c.Header("Access-Control-Allow-Headers", "X-Access-Token") | ||||||
|  | 	}) | ||||||
|  | 
 | ||||||
| 	api.registerRoutes() | 	api.registerRoutes() | ||||||
| 
 | 
 | ||||||
| 	listenString := fmt.Sprintf("%s:%d", viper.GetString(config.APIHost), viper.GetInt(config.APIPort)) | 	listenString := fmt.Sprintf("%s:%d", viper.GetString(config.APIHost), viper.GetInt(config.APIPort)) | ||||||
|  |  | ||||||
|  | @ -55,7 +55,7 @@ func (a *authorizationManager) HasPermission(permission string) bool { | ||||||
| 		return config.ContainsAuthKey(a.token) | 		return config.ContainsAuthKey(a.token) | ||||||
| 	} | 	} | ||||||
| 	if prefix == "s" { | 	if prefix == "s" { | ||||||
| 		return a.server.HasPermission(a.token, permission) | 		return a.server.HasPermission(a.token, permission) || config.ContainsAuthKey(a.token) | ||||||
| 	} | 	} | ||||||
| 	return false | 	return false | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -2,6 +2,7 @@ package api | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"net/http" | 	"net/http" | ||||||
|  | 	"runtime" | ||||||
| 
 | 
 | ||||||
| 	"github.com/Pterodactyl/wings/constants" | 	"github.com/Pterodactyl/wings/constants" | ||||||
| 	"github.com/gin-gonic/gin" | 	"github.com/gin-gonic/gin" | ||||||
|  | @ -35,16 +36,18 @@ func handleGetIndex(c *gin.Context) { | ||||||
| 			System  struct { | 			System  struct { | ||||||
| 				SystemType string `json:"type"` | 				SystemType string `json:"type"` | ||||||
| 				Platform   string `json:"platform"` | 				Platform   string `json:"platform"` | ||||||
|  | 				Arch       string `json:"arch"` | ||||||
| 				Release    string `json:"release"` | 				Release    string `json:"release"` | ||||||
| 				Cpus       int32  `json:"cpus"` | 				Cpus       int32  `json:"cpus"` | ||||||
| 				Freemem    uint64 `json:"freemem"` | 				Freemem    uint64 `json:"freemem"` | ||||||
| 			} `json:"os"` | 			} `json:"system"` | ||||||
| 		}{ | 		}{ | ||||||
| 			Name:    "Pterodactyl wings", | 			Name:    "Pterodactyl wings", | ||||||
| 			Version: constants.Version, | 			Version: constants.Version, | ||||||
| 		} | 		} | ||||||
| 		info.System.SystemType = hostInfo.OS | 		info.System.SystemType = hostInfo.OS | ||||||
| 		info.System.Platform = hostInfo.Platform | 		info.System.Platform = hostInfo.Platform | ||||||
|  | 		info.System.Arch = runtime.GOARCH | ||||||
| 		info.System.Release = hostInfo.KernelVersion | 		info.System.Release = hostInfo.KernelVersion | ||||||
| 		info.System.Cpus = cpuInfo[0].Cores | 		info.System.Cpus = cpuInfo[0].Cores | ||||||
| 		info.System.Freemem = memInfo.Free | 		info.System.Freemem = memInfo.Free | ||||||
|  | @ -57,7 +60,65 @@ func handleGetIndex(c *gin.Context) { | ||||||
| 	c.String(http.StatusOK, constants.IndexPage) | 	c.String(http.StatusOK, constants.IndexPage) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | type incomingConfiguration struct { | ||||||
|  | 	Debug bool `mapstructure:"debug"` | ||||||
|  | 	Web   struct { | ||||||
|  | 		ListenHost string `mapstructure:"host"` | ||||||
|  | 		ListenPort int16  `mapstructure:"port"` | ||||||
|  | 		SSL        struct { | ||||||
|  | 			Enabled     bool   `mapstructure:"enabled"` | ||||||
|  | 			Certificate string `mapstructure:"certificate"` | ||||||
|  | 			Key         string `mapstructure:"key"` | ||||||
|  | 		} `mapstructure:"ssl"` | ||||||
|  | 
 | ||||||
|  | 		Uploads struct { | ||||||
|  | 			MaximumSize int64 `mapstructure:"maximumSize"` | ||||||
|  | 		} `mapstructure:"uploads"` | ||||||
|  | 	} `mapstructure:"web"` | ||||||
|  | 
 | ||||||
|  | 	Docker struct { | ||||||
|  | 		Socket           string `mapstructure:"socket"` | ||||||
|  | 		AutoupdateImages bool   `mapstructure:"autoupdateImages"` | ||||||
|  | 		NetworkInterface string `mapstructure:"networkInterface"` | ||||||
|  | 		TimezonePath     string `mapstructure:"timezonePath"` | ||||||
|  | 	} `mapstructure:"docker"` | ||||||
|  | 
 | ||||||
|  | 	Sftp struct { | ||||||
|  | 		Path string `mapstructure:"path"` | ||||||
|  | 		Port int16  `mapstructure:"port"` | ||||||
|  | 	} `mapstructure:"sftp"` | ||||||
|  | 
 | ||||||
|  | 	Query struct { | ||||||
|  | 		KillOnFail bool `mapstructure:"killOnFail"` | ||||||
|  | 		FailLimit  bool `mapstructure:"failLimit"` | ||||||
|  | 	} `mapstructure:"query"` | ||||||
|  | 
 | ||||||
|  | 	Remote string `mapstructure:"remote"` | ||||||
|  | 
 | ||||||
|  | 	Log struct { | ||||||
|  | 		Path            string `mapstructure:"path"` | ||||||
|  | 		Level           string `mapstructure:"level"` | ||||||
|  | 		DeleteAfterDays int    `mapstructure:"deleteAfterDays"` | ||||||
|  | 	} `mapstructure:"log"` | ||||||
|  | 
 | ||||||
|  | 	AuthKeys []string `mapstructure:"authKeys"` | ||||||
|  | } | ||||||
|  | 
 | ||||||
| // handlePatchConfig handles PATCH /config
 | // handlePatchConfig handles PATCH /config
 | ||||||
| func handlePatchConfig(c *gin.Context) { | func handlePatchConfig(c *gin.Context) { | ||||||
| 
 | 	// reqBody, err := ioutil.ReadAll(c.Request.Body)
 | ||||||
|  | 	// if err != nil {
 | ||||||
|  | 	// 	log.WithError(err).Error("Failed to read input.")
 | ||||||
|  | 	// 	return
 | ||||||
|  | 	// }
 | ||||||
|  | 	// reqJSON := new(incomingConfiguration)
 | ||||||
|  | 	// err = json.Unmarshal(reqBody, reqJSON)
 | ||||||
|  | 	// if err != nil {
 | ||||||
|  | 	// 	log.WithError(err).Error("Failed to decode JSON.")
 | ||||||
|  | 	// 	return
 | ||||||
|  | 	// }
 | ||||||
|  | 	var json incomingConfiguration | ||||||
|  | 	if err := c.BindJSON(&json); err != nil { | ||||||
|  | 		log.WithError(err).Error("Failed to bind Json.") | ||||||
|  | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -1,27 +1,94 @@ | ||||||
| package api | package api | ||||||
| 
 | 
 | ||||||
| import "github.com/gin-gonic/gin" | import ( | ||||||
|  | 	"net/http" | ||||||
| 
 | 
 | ||||||
|  | 	"github.com/Pterodactyl/wings/control" | ||||||
|  | 	"github.com/gin-gonic/gin" | ||||||
|  | 	log "github.com/sirupsen/logrus" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | // GET /servers
 | ||||||
|  | // TODO: make jsonapi compliant
 | ||||||
| func handleGetServers(c *gin.Context) { | func handleGetServers(c *gin.Context) { | ||||||
| 
 | 	servers := control.GetServers() | ||||||
|  | 	c.JSON(http.StatusOK, servers) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | // POST /servers
 | ||||||
|  | // TODO: make jsonapi compliant
 | ||||||
| func handlePostServers(c *gin.Context) { | func handlePostServers(c *gin.Context) { | ||||||
| 
 | 	server := control.ServerStruct{} | ||||||
| } | 	if err := c.BindJSON(&server); err != nil { | ||||||
| 
 | 		log.WithField("server", server).WithError(err).Error("Failed to parse server request.") | ||||||
| func handleDeleteServers(c *gin.Context) { | 		c.Status(http.StatusBadRequest) | ||||||
| 
 | 		return | ||||||
|  | 	} | ||||||
|  | 	var srv control.Server | ||||||
|  | 	var err error | ||||||
|  | 	if srv, err = control.CreateServer(&server); err != nil { | ||||||
|  | 		if _, ok := err.(control.ErrServerExists); ok { | ||||||
|  | 			log.WithError(err).Error("Cannot create server, it already exists.") | ||||||
|  | 			c.Status(http.StatusBadRequest) | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  | 		log.WithField("server", server).WithError(err).Error("Failed to create server.") | ||||||
|  | 		c.Status(http.StatusInternalServerError) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	go func() { | ||||||
|  | 		env, err := srv.Environment() | ||||||
|  | 		if err != nil { | ||||||
|  | 			log.WithField("server", srv).WithError(err).Error("Failed to get server environment.") | ||||||
|  | 		} | ||||||
|  | 		env.Create() | ||||||
|  | 	}() | ||||||
|  | 	c.JSON(http.StatusOK, srv) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | // GET /servers/:server
 | ||||||
|  | // TODO: make jsonapi compliant
 | ||||||
| func handleGetServer(c *gin.Context) { | func handleGetServer(c *gin.Context) { | ||||||
| 
 | 	id := c.Param("server") | ||||||
|  | 	server := control.GetServer(id) | ||||||
|  | 	if server == nil { | ||||||
|  | 		c.Status(http.StatusNotFound) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	c.JSON(http.StatusOK, server) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | // PATCH /servers/:server
 | ||||||
| func handlePatchServer(c *gin.Context) { | func handlePatchServer(c *gin.Context) { | ||||||
| 
 | 
 | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | // DELETE /servers/:server
 | ||||||
|  | // TODO: make jsonapi compliant
 | ||||||
|  | func handleDeleteServer(c *gin.Context) { | ||||||
|  | 	id := c.Param("server") | ||||||
|  | 	server := control.GetServer(id) | ||||||
|  | 	if server == nil { | ||||||
|  | 		c.Status(http.StatusNotFound) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	env, err := server.Environment() | ||||||
|  | 	if err != nil { | ||||||
|  | 		log.WithError(err).WithField("server", server).Error("Failed to delete server.") | ||||||
|  | 	} | ||||||
|  | 	if err := env.Destroy(); err != nil { | ||||||
|  | 		log.WithError(err).Error("Failed to delete server, the environment couldn't be destroyed.") | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if err := control.DeleteServer(id); err != nil { | ||||||
|  | 		log.WithError(err).Error("Failed to delete server.") | ||||||
|  | 		c.Status(http.StatusInternalServerError) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	c.Status(http.StatusOK) | ||||||
|  | } | ||||||
|  | 
 | ||||||
| func handlePostServerReinstall(c *gin.Context) { | func handlePostServerReinstall(c *gin.Context) { | ||||||
| 
 | 
 | ||||||
| } | } | ||||||
|  | @ -34,12 +101,67 @@ func handlePostServerRebuild(c *gin.Context) { | ||||||
| 
 | 
 | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func handlePutServerPower(c *gin.Context) { | // POST /servers/:server/power
 | ||||||
|  | // TODO: make jsonapi compliant
 | ||||||
|  | func handlePostServerPower(c *gin.Context) { | ||||||
|  | 	server := getServerFromContext(c) | ||||||
|  | 	if server == nil { | ||||||
|  | 		c.Status(http.StatusNotFound) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
| 
 | 
 | ||||||
|  | 	auth := GetContextAuthManager(c) | ||||||
|  | 	if auth == nil { | ||||||
|  | 		c.Status(http.StatusInternalServerError) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	switch c.Query("action") { | ||||||
|  | 	case "start": | ||||||
|  | 		{ | ||||||
|  | 			if !auth.HasPermission("s:power:start") { | ||||||
|  | 				c.Status(http.StatusForbidden) | ||||||
|  | 				return | ||||||
|  | 			} | ||||||
|  | 			server.Start() | ||||||
|  | 		} | ||||||
|  | 	case "stop": | ||||||
|  | 		{ | ||||||
|  | 			if !auth.HasPermission("s:power:stop") { | ||||||
|  | 				c.Status(http.StatusForbidden) | ||||||
|  | 				return | ||||||
|  | 			} | ||||||
|  | 			server.Stop() | ||||||
|  | 		} | ||||||
|  | 	case "restart": | ||||||
|  | 		{ | ||||||
|  | 			if !auth.HasPermission("s:power:restart") { | ||||||
|  | 				c.Status(http.StatusForbidden) | ||||||
|  | 				return | ||||||
|  | 			} | ||||||
|  | 			server.Restart() | ||||||
|  | 		} | ||||||
|  | 	case "kill": | ||||||
|  | 		{ | ||||||
|  | 			if !auth.HasPermission("s:power:kill") { | ||||||
|  | 				c.Status(http.StatusForbidden) | ||||||
|  | 				return | ||||||
|  | 			} | ||||||
|  | 			server.Kill() | ||||||
|  | 		} | ||||||
|  | 	default: | ||||||
|  | 		{ | ||||||
|  | 			c.Status(http.StatusBadRequest) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | // POST /servers/:server/command
 | ||||||
|  | // TODO: make jsonapi compliant
 | ||||||
| func handlePostServerCommand(c *gin.Context) { | func handlePostServerCommand(c *gin.Context) { | ||||||
| 
 | 	server := getServerFromContext(c) | ||||||
|  | 	cmd := c.Query("command") | ||||||
|  | 	server.Exec(cmd) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func handleGetServerLog(c *gin.Context) { | func handleGetServerLog(c *gin.Context) { | ||||||
|  |  | ||||||
							
								
								
									
										103
									
								
								api/handlers_server_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										103
									
								
								api/handlers_server_test.go
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,103 @@ | ||||||
|  | package api | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"bytes" | ||||||
|  | 	"encoding/json" | ||||||
|  | 	"io/ioutil" | ||||||
|  | 	"net/http" | ||||||
|  | 	"net/http/httptest" | ||||||
|  | 	"testing" | ||||||
|  | 
 | ||||||
|  | 	"github.com/Pterodactyl/wings/control" | ||||||
|  | 	"github.com/gin-gonic/gin" | ||||||
|  | 	"github.com/stretchr/testify/assert" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | func TestHandleGetServers(t *testing.T) { | ||||||
|  | 	router := gin.New() | ||||||
|  | 	req, _ := http.NewRequest("GET", "/servers", nil) | ||||||
|  | 
 | ||||||
|  | 	router.GET("/servers", handleGetServers) | ||||||
|  | 
 | ||||||
|  | 	t.Run("returns an empty json array when no servers are configured", func(t *testing.T) { | ||||||
|  | 		rec := httptest.NewRecorder() | ||||||
|  | 		router.ServeHTTP(rec, req) | ||||||
|  | 		body, err := ioutil.ReadAll(rec.Body) | ||||||
|  | 		assert.Equal(t, http.StatusOK, rec.Code) | ||||||
|  | 		assert.Nil(t, err) | ||||||
|  | 		assert.Equal(t, "[]\n", string(body)) | ||||||
|  | 	}) | ||||||
|  | 
 | ||||||
|  | 	t.Run("returns an array of servers", func(t *testing.T) { | ||||||
|  | 
 | ||||||
|  | 	}) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | var testServer = control.ServerStruct{ | ||||||
|  | 	ID: "id1", | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func TestHandlePostServer(t *testing.T) { | ||||||
|  | 	// We need to decide how deep we want to go with testing here
 | ||||||
|  | 	// Should we just verify it works in general or test validation and
 | ||||||
|  | 	// minimal required config options as well?
 | ||||||
|  | 	// This will also vary depending on the Environment the server runs in
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func TestHandleGetServer(t *testing.T) { | ||||||
|  | 	router := gin.New() | ||||||
|  | 	router.GET("/servers/:server", handleGetServer) | ||||||
|  | 
 | ||||||
|  | 	control.CreateServer(&testServer) | ||||||
|  | 
 | ||||||
|  | 	t.Run("returns not found when the server doesn't exist", func(t *testing.T) { | ||||||
|  | 		req, _ := http.NewRequest("GET", "/servers/id0", nil) | ||||||
|  | 		rec := httptest.NewRecorder() | ||||||
|  | 		router.ServeHTTP(rec, req) | ||||||
|  | 		assert.Equal(t, http.StatusNotFound, rec.Code) | ||||||
|  | 	}) | ||||||
|  | 
 | ||||||
|  | 	t.Run("returns the server as json", func(t *testing.T) { | ||||||
|  | 		req, _ := http.NewRequest("GET", "/servers/id1", nil) | ||||||
|  | 		rec := httptest.NewRecorder() | ||||||
|  | 		router.ServeHTTP(rec, req) | ||||||
|  | 		assert.Equal(t, http.StatusOK, rec.Code) | ||||||
|  | 	}) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func TestHandlePatchServer(t *testing.T) { | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func TestHandleDeleteServer(t *testing.T) { | ||||||
|  | 	router := gin.New() | ||||||
|  | 	router.DELETE("/servers/:server", handleDeleteServer) | ||||||
|  | 
 | ||||||
|  | 	control.CreateServer(&testServer) | ||||||
|  | 
 | ||||||
|  | 	t.Run("returns not found when the server doesn't exist", func(t *testing.T) { | ||||||
|  | 		req, _ := http.NewRequest("DELETE", "/servers/id0", nil) | ||||||
|  | 		rec := httptest.NewRecorder() | ||||||
|  | 		router.ServeHTTP(rec, req) | ||||||
|  | 		assert.Equal(t, http.StatusNotFound, rec.Code) | ||||||
|  | 	}) | ||||||
|  | 
 | ||||||
|  | 	t.Run("deletes the server", func(t *testing.T) { | ||||||
|  | 		req, _ := http.NewRequest("DELETE", "/servers/id1", nil) | ||||||
|  | 		rec := httptest.NewRecorder() | ||||||
|  | 		router.ServeHTTP(rec, req) | ||||||
|  | 		assert.Equal(t, http.StatusOK, rec.Code) | ||||||
|  | 	}) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func bodyToJSON(body *bytes.Buffer, v interface{}) error { | ||||||
|  | 	reqBody, err := ioutil.ReadAll(body) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	err = json.Unmarshal(reqBody, v) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | @ -12,7 +12,7 @@ func (api *API) registerServerRoutes() { | ||||||
| 	api.router.POST("/servers/:server/reinstall", AuthHandler("s:install-server"), handlePostServerReinstall) | 	api.router.POST("/servers/:server/reinstall", AuthHandler("s:install-server"), handlePostServerReinstall) | ||||||
| 	api.router.POST("/servers/:server/rebuild", AuthHandler("g:server:rebuild"), handlePostServerRebuild) | 	api.router.POST("/servers/:server/rebuild", AuthHandler("g:server:rebuild"), handlePostServerRebuild) | ||||||
| 	api.router.POST("/servers/:server/password", AuthHandler(""), handlePostServerPassword) | 	api.router.POST("/servers/:server/password", AuthHandler(""), handlePostServerPassword) | ||||||
| 	api.router.POST("/servers/:server/power", AuthHandler("s:power"), handlePutServerPower) | 	api.router.POST("/servers/:server/power", AuthHandler("s:power"), handlePostServerPower) | ||||||
| 	api.router.POST("/servers/:server/command", AuthHandler("s:command"), handlePostServerCommand) | 	api.router.POST("/servers/:server/command", AuthHandler("s:command"), handlePostServerCommand) | ||||||
| 	api.router.GET("/servers/:server/log", AuthHandler("s:console"), handleGetServerLog) | 	api.router.GET("/servers/:server/log", AuthHandler("s:console"), handleGetServerLog) | ||||||
| 	api.router.POST("/servers/:server/suspend", AuthHandler(""), handlePostServerSuspend) | 	api.router.POST("/servers/:server/suspend", AuthHandler(""), handlePostServerSuspend) | ||||||
|  |  | ||||||
							
								
								
									
										10
									
								
								api/utils.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								api/utils.go
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,10 @@ | ||||||
|  | package api | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"github.com/Pterodactyl/wings/control" | ||||||
|  | 	"github.com/gin-gonic/gin" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | func getServerFromContext(context *gin.Context) control.Server { | ||||||
|  | 	return control.GetServer(context.Param("server")) | ||||||
|  | } | ||||||
|  | @ -1,10 +1,16 @@ | ||||||
| package command | package command | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
|  | 	"path/filepath" | ||||||
|  | 	"strconv" | ||||||
|  | 
 | ||||||
|  | 	"github.com/spf13/viper" | ||||||
|  | 
 | ||||||
| 	"github.com/Pterodactyl/wings/api" | 	"github.com/Pterodactyl/wings/api" | ||||||
| 	"github.com/Pterodactyl/wings/config" | 	"github.com/Pterodactyl/wings/config" | ||||||
| 	"github.com/Pterodactyl/wings/constants" | 	"github.com/Pterodactyl/wings/constants" | ||||||
| 	"github.com/Pterodactyl/wings/tools" | 	"github.com/Pterodactyl/wings/control" | ||||||
|  | 	"github.com/Pterodactyl/wings/utils" | ||||||
| 	log "github.com/sirupsen/logrus" | 	log "github.com/sirupsen/logrus" | ||||||
| 	"github.com/spf13/cobra" | 	"github.com/spf13/cobra" | ||||||
| ) | ) | ||||||
|  | @ -30,7 +36,7 @@ func Execute() { | ||||||
| 
 | 
 | ||||||
| func run(cmd *cobra.Command, args []string) { | func run(cmd *cobra.Command, args []string) { | ||||||
| 	tools.InitLogging() | 	tools.InitLogging() | ||||||
| 	log.Info("Loading configuration") | 	log.Info("Loading configuration...") | ||||||
| 	if err := config.LoadConfiguration(configPath); err != nil { | 	if err := config.LoadConfiguration(configPath); err != nil { | ||||||
| 		log.WithError(err).Fatal("Failed to find configuration file") | 		log.WithError(err).Fatal("Failed to find configuration file") | ||||||
| 	} | 	} | ||||||
|  | @ -46,7 +52,17 @@ func run(cmd *cobra.Command, args []string) { | ||||||
| 
 | 
 | ||||||
| 	log.Info("Configuration loaded successfully.") | 	log.Info("Configuration loaded successfully.") | ||||||
| 
 | 
 | ||||||
| 	log.Info("Starting api webserver") | 	log.Info("Loading configured servers...") | ||||||
|  | 	if err := control.LoadServerConfigurations(filepath.Join(viper.GetString(config.DataPath), constants.ServersPath)); err != nil { | ||||||
|  | 		log.WithError(err).Error("Failed to load configured servers.") | ||||||
|  | 	} | ||||||
|  | 	if amount := len(control.GetServers()); amount == 1 { | ||||||
|  | 		log.Info("Loaded 1 server.") | ||||||
|  | 	} else { | ||||||
|  | 		log.Info("Loaded " + strconv.Itoa(amount) + " servers.") | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	log.Info("Starting api webserver...") | ||||||
| 	api := api.NewAPI() | 	api := api.NewAPI() | ||||||
| 	api.Listen() | 	api.Listen() | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -1,6 +1,6 @@ | ||||||
| { | { | ||||||
|   "debug": false, |   "debug": false, | ||||||
|   "dataPath": "/srv/daemon-data", |   "dataPath": "/srv/wings", | ||||||
|   "api": { |   "api": { | ||||||
|     "host": "0.0.0.0", |     "host": "0.0.0.0", | ||||||
|     "port": 8080, |     "port": 8080, | ||||||
|  | @ -30,7 +30,7 @@ | ||||||
|   }, |   }, | ||||||
|   "remote": "https://pterodactyl.app", |   "remote": "https://pterodactyl.app", | ||||||
|   "log": { |   "log": { | ||||||
|     "path": "/srv/daemon-data/logs/", |     "path": "/srv/wings/logs/", | ||||||
|     "level": "info", |     "level": "info", | ||||||
|     "deleteAfterDays": 100 |     "deleteAfterDays": 100 | ||||||
|   }, |   }, | ||||||
|  |  | ||||||
|  | @ -1,4 +1,28 @@ | ||||||
| package constants | package constants | ||||||
| 
 | 
 | ||||||
| // Version is the current wings version
 | import "os" | ||||||
|  | 
 | ||||||
|  | // Version is the current wings version.
 | ||||||
| const Version = "0.0.1-alpha" | const Version = "0.0.1-alpha" | ||||||
|  | 
 | ||||||
|  | /* ---------- PATHS ---------- */ | ||||||
|  | 
 | ||||||
|  | // DefaultFilePerms are the file perms used for created files.
 | ||||||
|  | const DefaultFilePerms os.FileMode = 0644 | ||||||
|  | 
 | ||||||
|  | // DefaultFolderPerms are the file perms used for created folders.
 | ||||||
|  | const DefaultFolderPerms os.FileMode = 0744 | ||||||
|  | 
 | ||||||
|  | // ServersPath is the path of the servers within the configured DataPath.
 | ||||||
|  | const ServersPath = "servers" | ||||||
|  | 
 | ||||||
|  | // ServerConfigFile is the filename of the server config file.
 | ||||||
|  | const ServerConfigFile = "server.json" | ||||||
|  | 
 | ||||||
|  | // ServerDataPath is the path of the data of a single server.
 | ||||||
|  | const ServerDataPath = "data" | ||||||
|  | 
 | ||||||
|  | /* ---------- MISC ---------- */ | ||||||
|  | 
 | ||||||
|  | // JSONIndent is the indent to use with the json.MarshalIndent() function.
 | ||||||
|  | const JSONIndent = "  " | ||||||
|  |  | ||||||
|  | @ -2,8 +2,12 @@ package control | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"context" | 	"context" | ||||||
|  | 	"io" | ||||||
|  | 	"os" | ||||||
| 	"strings" | 	"strings" | ||||||
| 
 | 
 | ||||||
|  | 	"github.com/Pterodactyl/wings/constants" | ||||||
|  | 
 | ||||||
| 	"github.com/fsouza/go-dockerclient" | 	"github.com/fsouza/go-dockerclient" | ||||||
| 	log "github.com/sirupsen/logrus" | 	log "github.com/sirupsen/logrus" | ||||||
| ) | ) | ||||||
|  | @ -15,6 +19,10 @@ type dockerEnvironment struct { | ||||||
| 	container *docker.Container | 	container *docker.Container | ||||||
| 	context   context.Context | 	context   context.Context | ||||||
| 
 | 
 | ||||||
|  | 	containerInput  io.Writer | ||||||
|  | 	containerOutput io.Writer | ||||||
|  | 	closeWaiter     docker.CloseWaiter | ||||||
|  | 
 | ||||||
| 	server *ServerStruct | 	server *ServerStruct | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -38,16 +46,17 @@ func NewDockerEnvironment(server *ServerStruct) (Environment, error) { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if env.server.DockerContainer.ID != "" { | 	if env.server.DockerContainer.ID != "" { | ||||||
| 		if err := env.reattach(); err != nil { | 		if err := env.checkContainerExists(); err != nil { | ||||||
| 			log.WithError(err).Error("Failed to reattach to existing container.") | 			log.WithError(err).Error("Failed to find the container with stored id, removing id.") | ||||||
| 			return nil, err | 			env.server.DockerContainer.ID = "" | ||||||
|  | 			env.server.Save() | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	return &env, nil | 	return &env, nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (env *dockerEnvironment) reattach() error { | func (env *dockerEnvironment) checkContainerExists() error { | ||||||
| 	container, err := env.client.InspectContainer(env.server.DockerContainer.ID) | 	container, err := env.client.InspectContainer(env.server.DockerContainer.ID) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return err | 		return err | ||||||
|  | @ -56,10 +65,31 @@ func (env *dockerEnvironment) reattach() error { | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | func (env *dockerEnvironment) attach() error { | ||||||
|  | 	pr, pw := io.Pipe() | ||||||
|  | 
 | ||||||
|  | 	success := make(chan struct{}) | ||||||
|  | 	w, err := env.client.AttachToContainerNonBlocking(docker.AttachToContainerOptions{ | ||||||
|  | 		Container:    env.server.DockerContainer.ID, | ||||||
|  | 		InputStream:  pr, | ||||||
|  | 		OutputStream: os.Stdout, | ||||||
|  | 		Stdin:        true, | ||||||
|  | 		Stdout:       true, | ||||||
|  | 		Stream:       true, | ||||||
|  | 		Success:      success, | ||||||
|  | 	}) | ||||||
|  | 	env.closeWaiter = w | ||||||
|  | 	env.containerInput = pw | ||||||
|  | 
 | ||||||
|  | 	<-success | ||||||
|  | 	close(success) | ||||||
|  | 	return err | ||||||
|  | } | ||||||
|  | 
 | ||||||
| // Create creates the docker container for the environment and applies all
 | // Create creates the docker container for the environment and applies all
 | ||||||
| // settings to it
 | // settings to it
 | ||||||
| func (env *dockerEnvironment) Create() error { | func (env *dockerEnvironment) Create() error { | ||||||
| 	log.WithField("serverID", env.server.ID).Debug("Creating docker environment") | 	log.WithField("server", env.server.ID).Debug("Creating docker environment") | ||||||
| 	// Split image repository and tag to feed it to the library
 | 	// Split image repository and tag to feed it to the library
 | ||||||
| 	imageParts := strings.Split(env.server.Service().DockerImage, ":") | 	imageParts := strings.Split(env.server.Service().DockerImage, ":") | ||||||
| 	imageRepoParts := strings.Split(imageParts[0], "/") | 	imageRepoParts := strings.Split(imageParts[0], "/") | ||||||
|  | @ -77,18 +107,25 @@ func (env *dockerEnvironment) Create() error { | ||||||
| 	log.WithField("image", env.server.service.DockerImage).Debug("Pulling docker image") | 	log.WithField("image", env.server.service.DockerImage).Debug("Pulling docker image") | ||||||
| 	err := env.client.PullImage(pullImageOpts, docker.AuthConfiguration{}) | 	err := env.client.PullImage(pullImageOpts, docker.AuthConfiguration{}) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		log.WithError(err).WithField("serverID", env.server.ID).Error("Failed to create docker environment") | 		log.WithError(err).WithField("server", env.server.ID).Error("Failed to create docker environment") | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if err := os.MkdirAll(env.server.dataPath(), constants.DefaultFolderPerms); err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	// Create docker container
 | 	// Create docker container
 | ||||||
| 	// TODO: apply cpu, io, disk limits.
 | 	// TODO: apply cpu, io, disk limits.
 | ||||||
| 	containerConfig := &docker.Config{ | 	containerConfig := &docker.Config{ | ||||||
| 		Image: env.server.Service().DockerImage, | 		Image:     env.server.Service().DockerImage, | ||||||
|  | 		Cmd:       strings.Split(env.server.StartupCommand, " "), | ||||||
|  | 		OpenStdin: true, | ||||||
| 	} | 	} | ||||||
| 	containerHostConfig := &docker.HostConfig{ | 	containerHostConfig := &docker.HostConfig{ | ||||||
| 		Memory:     env.server.Settings.Memory, | 		Memory:     env.server.Settings.Memory, | ||||||
| 		MemorySwap: env.server.Settings.Swap, | 		MemorySwap: env.server.Settings.Swap, | ||||||
|  | 		Binds:      []string{env.server.dataPath() + ":/home/container"}, | ||||||
| 	} | 	} | ||||||
| 	createContainerOpts := docker.CreateContainerOptions{ | 	createContainerOpts := docker.CreateContainerOptions{ | ||||||
| 		Name:       "ptdl-" + env.server.UUIDShort(), | 		Name:       "ptdl-" + env.server.UUIDShort(), | ||||||
|  | @ -98,31 +135,55 @@ func (env *dockerEnvironment) Create() error { | ||||||
| 	} | 	} | ||||||
| 	container, err := env.client.CreateContainer(createContainerOpts) | 	container, err := env.client.CreateContainer(createContainerOpts) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		log.WithError(err).WithField("serverID", env.server.ID).Error("Failed to create docker container") | 		log.WithError(err).WithField("server", env.server.ID).Error("Failed to create docker container") | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| 	env.server.DockerContainer.ID = container.ID | 	env.server.DockerContainer.ID = container.ID | ||||||
|  | 	env.server.Save() | ||||||
| 	env.container = container | 	env.container = container | ||||||
| 
 | 
 | ||||||
|  | 	log.WithField("server", env.server.ID).Debug("Docker environment created") | ||||||
|  | 
 | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Destroy removes the environment's docker container
 | // Destroy removes the environment's docker container
 | ||||||
| func (env *dockerEnvironment) Destroy() error { | func (env *dockerEnvironment) Destroy() error { | ||||||
| 	log.WithField("serverID", env.server.ID).Debug("Destroying docker environment") | 	log.WithField("server", env.server.ID).Debug("Destroying docker environment") | ||||||
|  | 	if _, err := env.client.InspectContainer(env.server.DockerContainer.ID); err != nil { | ||||||
|  | 		if _, ok := err.(*docker.NoSuchContainer); ok { | ||||||
|  | 			log.WithField("server", env.server.ID).Debug("Container not found, docker environment destroyed already.") | ||||||
|  | 			return nil | ||||||
|  | 		} | ||||||
|  | 		log.WithError(err).WithField("server", env.server.ID).Error("Could not destroy docker environment") | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
| 	err := env.client.RemoveContainer(docker.RemoveContainerOptions{ | 	err := env.client.RemoveContainer(docker.RemoveContainerOptions{ | ||||||
| 		ID: env.server.DockerContainer.ID, | 		ID: env.server.DockerContainer.ID, | ||||||
| 	}) | 	}) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		log.WithError(err).WithField("serverID", env.server.ID).Error("Failed to destroy docker environment") | 		log.WithError(err).WithField("server", env.server.ID).Error("Failed to destroy docker environment") | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
|  | 
 | ||||||
|  | 	log.WithField("server", env.server.ID).Debug("Docker environment destroyed") | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | func (env *dockerEnvironment) Exists() bool { | ||||||
|  | 	if env.container != nil { | ||||||
|  | 		return true | ||||||
|  | 	} | ||||||
|  | 	env.checkContainerExists() | ||||||
|  | 	return env.container != nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
| // Start starts the environment's docker container
 | // Start starts the environment's docker container
 | ||||||
| func (env *dockerEnvironment) Start() error { | func (env *dockerEnvironment) Start() error { | ||||||
| 	log.WithField("serverID", env.server.ID).Debug("Starting service in docker environment") | 	log.WithField("server", env.server.ID).Debug("Starting service in docker environment") | ||||||
|  | 	if err := env.attach(); err != nil { | ||||||
|  | 		log.WithError(err).Error("Failed to attach to docker container") | ||||||
|  | 	} | ||||||
| 	if err := env.client.StartContainer(env.container.ID, nil); err != nil { | 	if err := env.client.StartContainer(env.container.ID, nil); err != nil { | ||||||
| 		log.WithError(err).Error("Failed to start docker container") | 		log.WithError(err).Error("Failed to start docker container") | ||||||
| 		return err | 		return err | ||||||
|  | @ -132,7 +193,7 @@ func (env *dockerEnvironment) Start() error { | ||||||
| 
 | 
 | ||||||
| // Stop stops the environment's docker container
 | // Stop stops the environment's docker container
 | ||||||
| func (env *dockerEnvironment) Stop() error { | func (env *dockerEnvironment) Stop() error { | ||||||
| 	log.WithField("serverID", env.server.ID).Debug("Stopping service in docker environment") | 	log.WithField("server", env.server.ID).Debug("Stopping service in docker environment") | ||||||
| 	if err := env.client.StopContainer(env.container.ID, 20000); err != nil { | 	if err := env.client.StopContainer(env.container.ID, 20000); err != nil { | ||||||
| 		log.WithError(err).Error("Failed to stop docker container") | 		log.WithError(err).Error("Failed to stop docker container") | ||||||
| 		return err | 		return err | ||||||
|  | @ -141,7 +202,7 @@ func (env *dockerEnvironment) Stop() error { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (env *dockerEnvironment) Kill() error { | func (env *dockerEnvironment) Kill() error { | ||||||
| 	log.WithField("serverID", env.server.ID).Debug("Killing service in docker environment") | 	log.WithField("server", env.server.ID).Debug("Killing service in docker environment") | ||||||
| 	if err := env.client.KillContainer(docker.KillContainerOptions{ | 	if err := env.client.KillContainer(docker.KillContainerOptions{ | ||||||
| 		ID: env.container.ID, | 		ID: env.container.ID, | ||||||
| 	}); err != nil { | 	}); err != nil { | ||||||
|  | @ -153,5 +214,7 @@ func (env *dockerEnvironment) Kill() error { | ||||||
| 
 | 
 | ||||||
| // Exec sends commands to the standard input of the docker container
 | // Exec sends commands to the standard input of the docker container
 | ||||||
| func (env *dockerEnvironment) Exec(command string) error { | func (env *dockerEnvironment) Exec(command string) error { | ||||||
| 	return nil | 	log.Debug("Command: " + command) | ||||||
|  | 	_, err := env.containerInput.Write([]byte(command + "\n")) | ||||||
|  | 	return err | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -4,22 +4,42 @@ import ( | ||||||
| 	"encoding/json" | 	"encoding/json" | ||||||
| 	"errors" | 	"errors" | ||||||
| 	"io/ioutil" | 	"io/ioutil" | ||||||
|  | 	"os" | ||||||
|  | 	"path/filepath" | ||||||
| 	"strings" | 	"strings" | ||||||
| 
 | 
 | ||||||
|  | 	"github.com/Pterodactyl/wings/config" | ||||||
|  | 	"github.com/Pterodactyl/wings/constants" | ||||||
| 	log "github.com/sirupsen/logrus" | 	log "github.com/sirupsen/logrus" | ||||||
|  | 	"github.com/spf13/viper" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
|  | // 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" | ||||||
|  | } | ||||||
|  | 
 | ||||||
| // Server is a Server
 | // Server is a Server
 | ||||||
| type Server interface { | type Server interface { | ||||||
| 	Start() error | 	Start() error | ||||||
| 	Stop() error | 	Stop() error | ||||||
|  | 	Restart() error | ||||||
|  | 	Kill() error | ||||||
| 	Exec(command string) error | 	Exec(command string) error | ||||||
| 	Rebuild() error | 	Rebuild() error | ||||||
| 
 | 
 | ||||||
|  | 	Save() error | ||||||
|  | 
 | ||||||
|  | 	Environment() (Environment, error) | ||||||
|  | 
 | ||||||
| 	HasPermission(string, string) bool | 	HasPermission(string, string) bool | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // server is a single instance of a Service managed by the panel
 | // ServerStruct is a single instance of a Service managed by the panel
 | ||||||
| type ServerStruct struct { | type ServerStruct struct { | ||||||
| 	// ID is the unique identifier of the server
 | 	// ID is the unique identifier of the server
 | ||||||
| 	ID string `json:"uuid"` | 	ID string `json:"uuid"` | ||||||
|  | @ -27,7 +47,7 @@ type ServerStruct struct { | ||||||
| 	// ServiceName is the name of the service. It is mainly used to allow storing the service
 | 	// ServiceName is the name of the service. It is mainly used to allow storing the service
 | ||||||
| 	// in the config
 | 	// in the config
 | ||||||
| 	ServiceName string `json:"serviceName"` | 	ServiceName string `json:"serviceName"` | ||||||
| 	service     *service | 	service     *Service | ||||||
| 	environment Environment | 	environment Environment | ||||||
| 
 | 
 | ||||||
| 	// StartupCommand is the command executed in the environment to start the server
 | 	// StartupCommand is the command executed in the environment to start the server
 | ||||||
|  | @ -88,8 +108,8 @@ func LoadServerConfigurations(path string) error { | ||||||
| 	servers = make(serversMap) | 	servers = make(serversMap) | ||||||
| 
 | 
 | ||||||
| 	for _, file := range serverFiles { | 	for _, file := range serverFiles { | ||||||
| 		if !file.IsDir() { | 		if file.IsDir() { | ||||||
| 			server, err := loadServerConfiguration(path + file.Name()) | 			server, err := loadServerConfiguration(filepath.Join(path, file.Name(), constants.ServerConfigFile)) | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				return err | 				return err | ||||||
| 			} | 			} | ||||||
|  | @ -114,6 +134,38 @@ func loadServerConfiguration(path string) (*ServerStruct, error) { | ||||||
| 	return server, nil | 	return server, nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | func storeServerConfiguration(server *ServerStruct) error { | ||||||
|  | 	serverJSON, err := json.MarshalIndent(server, "", constants.JSONIndent) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	if err := os.MkdirAll(server.path(), constants.DefaultFolderPerms); err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	if err := ioutil.WriteFile(server.configFilePath(), serverJSON, constants.DefaultFilePerms); err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func storeServerConfigurations() error { | ||||||
|  | 	for _, s := range servers { | ||||||
|  | 		if err := storeServerConfiguration(s); err != nil { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func deleteServerFolder(id string) error { | ||||||
|  | 	path := filepath.Join(viper.GetString(config.DataPath), constants.ServersPath, id) | ||||||
|  | 	folder, err := os.Stat(path) | ||||||
|  | 	if os.IsNotExist(err) || !folder.IsDir() { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	return os.RemoveAll(path) | ||||||
|  | } | ||||||
|  | 
 | ||||||
| // GetServers returns an array of all servers the daemon manages
 | // GetServers returns an array of all servers the daemon manages
 | ||||||
| func GetServers() []Server { | func GetServers() []Server { | ||||||
| 	serverArray := make([]Server, len(servers)) | 	serverArray := make([]Server, len(servers)) | ||||||
|  | @ -136,53 +188,83 @@ func GetServer(id string) Server { | ||||||
| 
 | 
 | ||||||
| // CreateServer creates a new server
 | // CreateServer creates a new server
 | ||||||
| func CreateServer(server *ServerStruct) (Server, error) { | func CreateServer(server *ServerStruct) (Server, error) { | ||||||
|  | 	if servers[server.ID] != nil { | ||||||
|  | 		return nil, ErrServerExists{server.ID} | ||||||
|  | 	} | ||||||
| 	servers[server.ID] = server | 	servers[server.ID] = server | ||||||
|  | 	if err := server.Save(); err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
| 	return server, nil | 	return server, nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // DeleteServer deletes a server and all related files
 | // DeleteServer deletes a server and all related files
 | ||||||
| // NOTE: This is not reversible.
 | // NOTE: This is not reversible.
 | ||||||
| func DeleteServer(uuid string) error { | func DeleteServer(id string) error { | ||||||
| 	delete(servers, uuid) | 	if err := deleteServerFolder(id); err != nil { | ||||||
|  | 		log.WithField("server", id).WithError(err).Error("Failed to delete server.") | ||||||
|  | 	} | ||||||
|  | 	delete(servers, id) | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (s *ServerStruct) Start() error { | func (s *ServerStruct) Start() error { | ||||||
| 	/*if err := s.Environment().Create(); err != nil { | 	env, err := s.Environment() | ||||||
|  | 	if err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| 	if err := s.Environment().Start(); err != nil { | 	if !env.Exists() { | ||||||
| 		return err | 		if err := env.Create(); err != nil { | ||||||
| 	}*/ | 			return err | ||||||
| 	return nil | 		} | ||||||
|  | 	} | ||||||
|  | 	return env.Start() | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (s *ServerStruct) Stop() error { | func (s *ServerStruct) Stop() error { | ||||||
| 	/*if err := s.Environment().Stop(); err != nil { | 	env, err := s.Environment() | ||||||
|  | 	if err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	}*/ | 	} | ||||||
| 	return nil | 	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() | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (s *ServerStruct) Exec(command string) error { | func (s *ServerStruct) Exec(command string) error { | ||||||
| 	/*if err := s.Environment().Exec(command); err != nil { | 	env, err := s.Environment() | ||||||
|  | 	if err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	}*/ | 	} | ||||||
| 	return nil | 	return env.Exec(command) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (s *ServerStruct) Rebuild() error { | func (s *ServerStruct) Rebuild() error { | ||||||
| 	/*if err := s.Environment().ReCreate(); err != nil { | 	env, err := s.Environment() | ||||||
|  | 	if err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	}*/ | 	} | ||||||
| 	return nil | 	return env.ReCreate() | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Service returns the server's service configuration
 | // Service returns the server's service configuration
 | ||||||
| func (s *ServerStruct) Service() *service { | func (s *ServerStruct) Service() *Service { | ||||||
| 	if s.service == nil { | 	if s.service == nil { | ||||||
| 		// TODO: Properly use the correct service, mock for now.
 | 		// TODO: Properly use the correct service, mock for now.
 | ||||||
| 		s.service = &service{ | 		s.service = &Service{ | ||||||
| 			DockerImage:     "quay.io/pterodactyl/core:java", | 			DockerImage:     "quay.io/pterodactyl/core:java", | ||||||
| 			EnvironmentName: "docker", | 			EnvironmentName: "docker", | ||||||
| 		} | 		} | ||||||
|  | @ -225,6 +307,22 @@ func (s *ServerStruct) HasPermission(token string, permission string) bool { | ||||||
| 	return false | 	return false | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (s *ServerStruct) save() { | func (s *ServerStruct) Save() error { | ||||||
| 
 | 	if err := storeServerConfiguration(s); err != nil { | ||||||
|  | 		log.WithField("server", s.ID).WithError(err).Error("Failed to store server configuration.") | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (s *ServerStruct) path() string { | ||||||
|  | 	return filepath.Join(viper.GetString(config.DataPath), constants.ServersPath, s.ID) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (s *ServerStruct) dataPath() string { | ||||||
|  | 	return filepath.Join(s.path(), constants.ServerDataPath) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (s *ServerStruct) configFilePath() string { | ||||||
|  | 	return filepath.Join(s.path(), constants.ServerConfigFile) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -1,6 +1,6 @@ | ||||||
| package control | package control | ||||||
| 
 | 
 | ||||||
| type service struct { | type Service struct { | ||||||
| 	server *Server | 	server *Server | ||||||
| 
 | 
 | ||||||
| 	// EnvironmentName is the name of the environment used by the service
 | 	// EnvironmentName is the name of the environment used by the service
 | ||||||
|  |  | ||||||
|  | @ -26,7 +26,6 @@ func InitLogging() { | ||||||
| 
 | 
 | ||||||
| // ConfigureLogging applies the configuration to the logging library.
 | // ConfigureLogging applies the configuration to the logging library.
 | ||||||
| func ConfigureLogging() error { | func ConfigureLogging() error { | ||||||
| 
 |  | ||||||
| 	path := filepath.Clean(viper.GetString(config.LogPath)) | 	path := filepath.Clean(viper.GetString(config.LogPath)) | ||||||
| 	if err := os.MkdirAll(path, constants.DefaultFolderPerms); err != nil { | 	if err := os.MkdirAll(path, constants.DefaultFolderPerms); err != nil { | ||||||
| 		return err | 		return err | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue
	
	Block a user