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,7 +107,11 @@ 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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -85,10 +119,13 @@ func (env *dockerEnvironment) Create() error {
|
||||||
// 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() {
|
||||||
|
if err := env.Create(); err != nil {
|
||||||
return err
|
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