added basic servers implementation
This commit is contained in:
parent
d1f1d4e7c9
commit
f57888cb8e
@ -1,6 +1,10 @@
|
|||||||
package dbhandler
|
package dbhandler
|
||||||
|
|
||||||
import "git.acooldomain.co/server-manager/backend-kubernetes-go/models"
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"git.acooldomain.co/server-manager/backend-kubernetes-go/models"
|
||||||
|
)
|
||||||
|
|
||||||
type Server struct {
|
type Server struct {
|
||||||
Id string
|
Id string
|
||||||
@ -20,9 +24,9 @@ type ServerUpdateRequest struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type ServersDbHandler interface {
|
type ServersDbHandler interface {
|
||||||
ListServers() ([]Server, error)
|
ListServers(ctx context.Context) ([]Server, error)
|
||||||
GetServer(serverId string) (*Server, error)
|
GetServer(ctx context.Context, serverId string) (*Server, error)
|
||||||
CreateServer(server Server) error
|
CreateServer(ctx context.Context, server Server) error
|
||||||
DeleteServer(serverId string) error
|
DeleteServer(ctx context.Context, serverId string) error
|
||||||
UpdateServer(serverId string, updateParams ServerUpdateRequest) error
|
UpdateServer(ctx context.Context, serverId string, updateParams ServerUpdateRequest) error
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,16 @@
|
|||||||
package dbhandler
|
package dbhandler
|
||||||
|
|
||||||
import "git.acooldomain.co/server-manager/backend-kubernetes-go/models"
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"git.acooldomain.co/server-manager/backend-kubernetes-go/models"
|
||||||
|
)
|
||||||
|
|
||||||
type ServersAuthorizationDbHandler interface {
|
type ServersAuthorizationDbHandler interface {
|
||||||
AddPermissions(username string, server_id string, permissions models.Permission) error
|
AddPermissions(ctx context.Context, username string, server_id string, permissions models.Permission) error
|
||||||
RemovePermissions(username string, server_id string, permissions models.Permission) error
|
RemovePermissions(ctx context.Context, string, server_id string, permissions models.Permission) error
|
||||||
SetPermissions(username string, server_id string, permissions models.Permission) error
|
SetPermissions(ctx context.Context, username string, server_id string, permissions models.Permission) error
|
||||||
GetPermissions(username string, server_id string) (models.Permission, error)
|
GetPermissions(ctx context.Context, username string, server_id string) (models.Permission, error)
|
||||||
RemoveUser(username string) error
|
RemoveUser(ctx context.Context, username string) error
|
||||||
RemoveServer(server_id string) error
|
RemoveServer(ctx context.Context, server_id string) error
|
||||||
}
|
}
|
||||||
|
@ -4,10 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
"log"
|
|
||||||
"net"
|
"net"
|
||||||
"strings"
|
|
||||||
|
|
||||||
instancemanager "git.acooldomain.co/server-manager/backend-kubernetes-go/instance_manager"
|
instancemanager "git.acooldomain.co/server-manager/backend-kubernetes-go/instance_manager"
|
||||||
"git.acooldomain.co/server-manager/backend-kubernetes-go/models"
|
"git.acooldomain.co/server-manager/backend-kubernetes-go/models"
|
||||||
@ -63,6 +60,22 @@ func (self *InstanceManager) getVolume(ctx context.Context, serverId string) (*v
|
|||||||
|
|
||||||
// General
|
// General
|
||||||
// Read Only
|
// Read Only
|
||||||
|
func (self *InstanceManager) GetImage(ctx context.Context, imageId string) (*instancemanager.Image, error) {
|
||||||
|
imageInspect, _, err := self.client.ImageInspectWithRaw(ctx, imageId)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if imageInspect.Config.Labels["type"] != "game" {
|
||||||
|
return nil, fmt.Errorf("Image not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
image := convertImageInspectToInstanceImage(imageInspect)
|
||||||
|
|
||||||
|
return &image, nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
func (self *InstanceManager) ListImages(ctx context.Context) ([]instancemanager.Image, error) {
|
func (self *InstanceManager) ListImages(ctx context.Context) ([]instancemanager.Image, error) {
|
||||||
imageFilters, err := convertLabelsToFilter(ImageLabels{Type: Game})
|
imageFilters, err := convertLabelsToFilter(ImageLabels{Type: Game})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -81,14 +94,7 @@ func (self *InstanceManager) ListImages(ctx context.Context) ([]instancemanager.
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
ports := convertImagePortsToPorts(imageInspect.Config.ExposedPorts)
|
images[i] = convertImageInspectToInstanceImage(imageInspect)
|
||||||
imageId := convertContainerImageToImage(rawImage.RepoTags[0])
|
|
||||||
images[i] = instancemanager.Image{
|
|
||||||
Registry: imageId.Registry,
|
|
||||||
Tag: imageId.Tag,
|
|
||||||
Ports: ports,
|
|
||||||
Command: strings.Join(imageInspect.Config.Cmd, " "),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return images, nil
|
return images, nil
|
||||||
@ -117,6 +123,7 @@ func (self *InstanceManager) GetServer(ctx context.Context, serverId string) (*i
|
|||||||
RunningCommand: "",
|
RunningCommand: "",
|
||||||
RunningImage: nil,
|
RunningImage: nil,
|
||||||
Ports: nil,
|
Ports: nil,
|
||||||
|
Domain: self.config.GamesDomain,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -125,7 +132,7 @@ func (self *InstanceManager) GetServer(ctx context.Context, serverId string) (*i
|
|||||||
running := serverContainer.State == "running"
|
running := serverContainer.State == "running"
|
||||||
|
|
||||||
runningCommand := serverContainer.Command
|
runningCommand := serverContainer.Command
|
||||||
image := convertContainerImageToImage(serverContainer.Image)
|
image := convertImageStringToModelsImage(serverContainer.Image)
|
||||||
|
|
||||||
return &instancemanager.Server{
|
return &instancemanager.Server{
|
||||||
Id: volume.Name,
|
Id: volume.Name,
|
||||||
@ -133,6 +140,7 @@ func (self *InstanceManager) GetServer(ctx context.Context, serverId string) (*i
|
|||||||
RunningCommand: runningCommand,
|
RunningCommand: runningCommand,
|
||||||
Ports: convertContainerPortsToPorts(serverContainer.Ports),
|
Ports: convertContainerPortsToPorts(serverContainer.Ports),
|
||||||
RunningImage: &image,
|
RunningImage: &image,
|
||||||
|
Domain: self.config.GamesDomain,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -156,6 +164,7 @@ func (self *InstanceManager) ListServers(ctx context.Context) ([]instancemanager
|
|||||||
RunningCommand: "",
|
RunningCommand: "",
|
||||||
Ports: nil,
|
Ports: nil,
|
||||||
RunningImage: nil,
|
RunningImage: nil,
|
||||||
|
Domain: self.config.GamesDomain,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -177,7 +186,7 @@ func (self *InstanceManager) ListServers(ctx context.Context) ([]instancemanager
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
image := convertContainerImageToImage(container.Image)
|
image := convertImageStringToModelsImage(container.Image)
|
||||||
|
|
||||||
serverStatus[containerLabels.VolumeId].Ports = convertContainerPortsToPorts(container.Ports)
|
serverStatus[containerLabels.VolumeId].Ports = convertContainerPortsToPorts(container.Ports)
|
||||||
serverStatus[containerLabels.VolumeId].Running = true
|
serverStatus[containerLabels.VolumeId].Running = true
|
||||||
@ -197,7 +206,12 @@ func (self *InstanceManager) ListServers(ctx context.Context) ([]instancemanager
|
|||||||
}
|
}
|
||||||
|
|
||||||
// State Changing
|
// State Changing
|
||||||
func (self *InstanceManager) StartServer(ctx context.Context, serverId string, image instancemanager.Image, command string, ports []models.Port) error {
|
func (self *InstanceManager) StartServer(ctx context.Context,
|
||||||
|
serverId string,
|
||||||
|
imageId string,
|
||||||
|
command string,
|
||||||
|
ports []models.Port,
|
||||||
|
) error {
|
||||||
server, err := self.GetServer(ctx, serverId)
|
server, err := self.GetServer(ctx, serverId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -207,7 +221,6 @@ func (self *InstanceManager) StartServer(ctx context.Context, serverId string, i
|
|||||||
return fmt.Errorf("Server %s already running", serverId)
|
return fmt.Errorf("Server %s already running", serverId)
|
||||||
}
|
}
|
||||||
|
|
||||||
imageId := image.Registry + ":" + image.Tag
|
|
||||||
containerLabels := ContainerLabels{
|
containerLabels := ContainerLabels{
|
||||||
VolumeId: server.Id,
|
VolumeId: server.Id,
|
||||||
Type: "GAME",
|
Type: "GAME",
|
||||||
@ -216,6 +229,10 @@ func (self *InstanceManager) StartServer(ctx context.Context, serverId string, i
|
|||||||
volumes := make(map[string]struct{})
|
volumes := make(map[string]struct{})
|
||||||
|
|
||||||
var portMapping nat.PortMap = make(nat.PortMap)
|
var portMapping nat.PortMap = make(nat.PortMap)
|
||||||
|
image, err := self.GetImage(ctx, imageId)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
if len(ports) == 0 {
|
if len(ports) == 0 {
|
||||||
for _, port := range image.Ports {
|
for _, port := range image.Ports {
|
||||||
@ -232,8 +249,6 @@ func (self *InstanceManager) StartServer(ctx context.Context, serverId string, i
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
rawImage, _, err := self.client.ImageInspectWithRaw(ctx, imageId)
|
|
||||||
|
|
||||||
if command == "" {
|
if command == "" {
|
||||||
command = image.Command
|
command = image.Command
|
||||||
}
|
}
|
||||||
@ -274,7 +289,7 @@ func (self *InstanceManager) StartServer(ctx context.Context, serverId string, i
|
|||||||
},
|
},
|
||||||
&container.HostConfig{
|
&container.HostConfig{
|
||||||
AutoRemove: true,
|
AutoRemove: true,
|
||||||
Mounts: []mount.Mount{{Source: server.Id, Target: rawImage.Config.WorkingDir, Type: "volume"}},
|
Mounts: []mount.Mount{{Source: server.Id, Target: image.WorkingDir, Type: "volume"}},
|
||||||
PortBindings: portMapping,
|
PortBindings: portMapping,
|
||||||
ConsoleSize: [2]uint{1000, 1000},
|
ConsoleSize: [2]uint{1000, 1000},
|
||||||
},
|
},
|
||||||
@ -328,6 +343,7 @@ func (self *InstanceManager) CreateServer(ctx context.Context) (*instancemanager
|
|||||||
Running: false,
|
Running: false,
|
||||||
RunningImage: nil,
|
RunningImage: nil,
|
||||||
RunningCommand: "",
|
RunningCommand: "",
|
||||||
|
Domain: self.config.GamesDomain,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,7 +4,6 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"reflect"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
instancemanager "git.acooldomain.co/server-manager/backend-kubernetes-go/instance_manager"
|
instancemanager "git.acooldomain.co/server-manager/backend-kubernetes-go/instance_manager"
|
||||||
@ -21,6 +20,7 @@ func convertLabelsToFilter(labels any) (*filters.Args, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
for key, value := range *labelMap {
|
for key, value := range *labelMap {
|
||||||
args.Add("label", fmt.Sprintf("%s=%s", key, value))
|
args.Add("label", fmt.Sprintf("%s=%s", key, value))
|
||||||
}
|
}
|
||||||
@ -29,7 +29,6 @@ func convertLabelsToFilter(labels any) (*filters.Args, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func convertLabelsToMap(labels any) (*map[string]string, error) {
|
func convertLabelsToMap(labels any) (*map[string]string, error) {
|
||||||
|
|
||||||
raw, err := json.Marshal(labels)
|
raw, err := json.Marshal(labels)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -80,7 +79,7 @@ func convertContainerPortsToPorts(ports []types.Port) []models.Port {
|
|||||||
return containerPorts
|
return containerPorts
|
||||||
}
|
}
|
||||||
|
|
||||||
func convertContainerImageToImage(image string) models.Image {
|
func convertImageStringToModelsImage(image string) models.Image {
|
||||||
imageSegments := strings.Split(image, ":")
|
imageSegments := strings.Split(image, ":")
|
||||||
imageRegistry := imageSegments[0]
|
imageRegistry := imageSegments[0]
|
||||||
imageTag := imageSegments[1]
|
imageTag := imageSegments[1]
|
||||||
@ -91,6 +90,20 @@ func convertContainerImageToImage(image string) models.Image {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func convertImageInspectToInstanceImage(image types.ImageInspect) instancemanager.Image {
|
||||||
|
modelsImage := convertImageStringToModelsImage(image.RepoTags[0])
|
||||||
|
|
||||||
|
ports := convertImagePortsToPorts(image.Config.ExposedPorts)
|
||||||
|
|
||||||
|
return instancemanager.Image{
|
||||||
|
Registry: modelsImage.Registry,
|
||||||
|
Tag: modelsImage.Tag,
|
||||||
|
Command: strings.Join(image.Config.Cmd, " "),
|
||||||
|
Ports: ports,
|
||||||
|
WorkingDir: image.Config.WorkingDir,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func convertContainerLabelsToStruct(labels map[string]string) (*ContainerLabels, error) {
|
func convertContainerLabelsToStruct(labels map[string]string) (*ContainerLabels, error) {
|
||||||
var containerLabels ContainerLabels
|
var containerLabels ContainerLabels
|
||||||
|
|
||||||
@ -104,6 +117,7 @@ func convertContainerLabelsToStruct(labels map[string]string) (*ContainerLabels,
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return &containerLabels, nil
|
return &containerLabels, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -120,6 +134,7 @@ func convertVolumeLabelsToStruct(labels map[string]string) (*VolumeLabels, error
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return &volumeLabels, nil
|
return &volumeLabels, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,6 +13,7 @@ type Server struct {
|
|||||||
RunningCommand string
|
RunningCommand string
|
||||||
RunningImage *models.Image
|
RunningImage *models.Image
|
||||||
Ports []models.Port
|
Ports []models.Port
|
||||||
|
Domain string
|
||||||
}
|
}
|
||||||
|
|
||||||
type Port struct {
|
type Port struct {
|
||||||
@ -24,18 +25,20 @@ type Image struct {
|
|||||||
Registry string
|
Registry string
|
||||||
Tag string
|
Tag string
|
||||||
Command string
|
Command string
|
||||||
|
WorkingDir string
|
||||||
Ports []Port
|
Ports []Port
|
||||||
}
|
}
|
||||||
|
|
||||||
type InstanceManager interface {
|
type InstanceManager interface {
|
||||||
//General
|
//General
|
||||||
// Read Only
|
// Read Only
|
||||||
|
GetImage(ctx context.Context, imageId string) (*Image, error)
|
||||||
ListImages(ctx context.Context) ([]Image, error)
|
ListImages(ctx context.Context) ([]Image, error)
|
||||||
GetServer(ctx context.Context, serverId string) (*Server, error)
|
GetServer(ctx context.Context, serverId string) (*Server, error)
|
||||||
ListServers(ctx context.Context) ([]Server, error)
|
ListServers(ctx context.Context) ([]Server, error)
|
||||||
|
|
||||||
// State Changing
|
// State Changing
|
||||||
StartServer(ctx context.Context, serverId string, image Image, command string, ports []models.Port) error
|
StartServer(ctx context.Context, serverId string, imageId string, command string, ports []models.Port) error
|
||||||
StopServer(ctx context.Context, serverId string) error
|
StopServer(ctx context.Context, serverId string) error
|
||||||
|
|
||||||
CreateServer(ctx context.Context) (*Server, error)
|
CreateServer(ctx context.Context) (*Server, error)
|
||||||
|
13
main.go
13
main.go
@ -18,14 +18,6 @@ import (
|
|||||||
|
|
||||||
const CONFIG_SECRET_NAME = "CONFIG_PATH"
|
const CONFIG_SECRET_NAME = "CONFIG_PATH"
|
||||||
|
|
||||||
func get_servers_db_handler(config models.ServersDatabaseConfig) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func get_users_db_handler(config models.UsersDatabaseConfig) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
router := gin.Default()
|
router := gin.Default()
|
||||||
|
|
||||||
@ -49,11 +41,6 @@ func main() {
|
|||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
switch config.UserDatabase.Mongo {
|
|
||||||
case nil:
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
|
|
||||||
client, err := dbhandler.Connect(config.UsersDatabase.Mongo)
|
client, err := dbhandler.Connect(config.UsersDatabase.Mongo)
|
||||||
defer func() {
|
defer func() {
|
||||||
if err = client.Disconnect(context.TODO()); err != nil {
|
if err = client.Disconnect(context.TODO()); err != nil {
|
||||||
|
@ -49,13 +49,15 @@ type FileBrowserConfig struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type DockerInstanceManagerConfig struct {
|
type DockerInstanceManagerConfig struct {
|
||||||
|
GamesDomain string `yaml:"games_domain"`
|
||||||
BrowsersDomain string `yaml:"browsers_domain"`
|
BrowsersDomain string `yaml:"browsers_domain"`
|
||||||
CertificateResolver string `yaml:"certificate_resolver"`
|
CertificateResolver string `yaml:"certificate_resolver"`
|
||||||
FileBrowser FileBrowserConfig `yaml:"file_browser"`
|
FileBrowser FileBrowserConfig `yaml:"file_browser"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type InstanceManagerConfig struct {
|
type InstanceManagerConfig struct {
|
||||||
Type InstanceManagerType
|
Type InstanceManagerType `yaml:"type"`
|
||||||
|
Docker DockerInstanceManagerConfig `yaml:"docker"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ServersDatabaseConfig struct {
|
type ServersDatabaseConfig struct {
|
||||||
|
@ -7,56 +7,21 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"git.acooldomain.co/server-manager/backend-kubernetes-go/auth"
|
"git.acooldomain.co/server-manager/backend-kubernetes-go/auth"
|
||||||
|
"git.acooldomain.co/server-manager/backend-kubernetes-go/db_handler/mongo"
|
||||||
|
"git.acooldomain.co/server-manager/backend-kubernetes-go/dbhandler"
|
||||||
|
instancemanager "git.acooldomain.co/server-manager/backend-kubernetes-go/instance_manager"
|
||||||
|
"git.acooldomain.co/server-manager/backend-kubernetes-go/instance_manager/docker"
|
||||||
"git.acooldomain.co/server-manager/backend-kubernetes-go/models"
|
"git.acooldomain.co/server-manager/backend-kubernetes-go/models"
|
||||||
"github.com/buildkite/shellwords"
|
|
||||||
"github.com/docker/docker/api/types"
|
|
||||||
"github.com/docker/docker/api/types/container"
|
|
||||||
"github.com/docker/docker/api/types/filters"
|
|
||||||
"github.com/docker/docker/api/types/image"
|
|
||||||
"github.com/docker/docker/api/types/mount"
|
|
||||||
"github.com/docker/docker/api/types/network"
|
|
||||||
"github.com/docker/docker/api/types/volume"
|
|
||||||
"github.com/docker/docker/client"
|
|
||||||
"github.com/docker/go-connections/nat"
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/gorilla/websocket"
|
"github.com/gorilla/websocket"
|
||||||
v1 "github.com/opencontainers/image-spec/specs-go/v1"
|
v1 "github.com/opencontainers/image-spec/specs-go/v1"
|
||||||
"go.mongodb.org/mongo-driver/bson"
|
|
||||||
"go.mongodb.org/mongo-driver/mongo"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const FILE_BROWSER_IMAGE = "filebrowser/filebrowser:latest"
|
|
||||||
|
|
||||||
var DOMAIN string = ""
|
|
||||||
|
|
||||||
type Connection struct {
|
|
||||||
databaseConnection *mongo.Client
|
|
||||||
dockerClient *client.Client
|
|
||||||
}
|
|
||||||
|
|
||||||
type ContainerLabels struct {
|
|
||||||
OwnerId string `json:"user_id"`
|
|
||||||
ImageId string `json:"image_id"`
|
|
||||||
VolumeId string `json:"volume_id"`
|
|
||||||
Type string `json:"type"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type VolumeLabels struct {
|
|
||||||
OwnerId string `json:"user_id"`
|
|
||||||
ImageId string `json:"image_id"`
|
|
||||||
Type string `json:"type"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type ImageLabels struct {
|
|
||||||
Type string `json:"type"`
|
|
||||||
}
|
|
||||||
|
|
||||||
var upgrader = websocket.Upgrader{
|
var upgrader = websocket.Upgrader{
|
||||||
ReadBufferSize: 1024,
|
ReadBufferSize: 1024,
|
||||||
WriteBufferSize: 1024,
|
WriteBufferSize: 1024,
|
||||||
@ -65,113 +30,34 @@ var upgrader = websocket.Upgrader{
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func (con Connection) getServerInfo(volume volume.Volume) (*models.ServerInfo, error) {
|
type Connection struct {
|
||||||
var volumeLabels VolumeLabels
|
ServersDbHandler dbhandler.ServersDbHandler
|
||||||
var serverData models.ServerData
|
InstanceManager instancemanager.InstanceManager
|
||||||
|
ServerAuthorization dbhandler.ServersAuthorizationDbHandler
|
||||||
con.databaseConnection.Database("Backend").Collection("Servers").FindOne(context.TODO(), bson.D{{Key: "Id", Value: volume.Name}}).Decode(&serverData)
|
|
||||||
jsonData, err := json.Marshal(volume.Labels)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
err = json.Unmarshal(jsonData, &volumeLabels)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
|
|
||||||
}
|
|
||||||
var imagePorts []models.Port
|
|
||||||
|
|
||||||
if len(serverData.DefaultPorts) == 0 {
|
|
||||||
|
|
||||||
imageList, err := con.dockerClient.ImageList(context.TODO(), image.ListOptions{Filters: filters.NewArgs(filters.Arg("reference", volumeLabels.ImageId), filters.Arg("label", "type=GAME"))})
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if len(imageList) == 0 {
|
|
||||||
return nil, fmt.Errorf("ImageId %s does not exist", volumeLabels.ImageId)
|
|
||||||
}
|
|
||||||
imageSummery := imageList[0]
|
|
||||||
imageInspect, _, err := con.dockerClient.ImageInspectWithRaw(context.TODO(), imageSummery.ID)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
imagePorts = make([]models.Port, len(imageInspect.Config.ExposedPorts))
|
type ImageInfo struct {
|
||||||
i := 0
|
Name string `json:"Name"`
|
||||||
for imagePort := range imageInspect.Config.ExposedPorts {
|
Version string `json:"Version"`
|
||||||
imagePorts[i] = models.Port{Protocol: imagePort.Proto(), Number: imagePort.Int()}
|
Ports []models.Port `json:"Ports"`
|
||||||
i += 1
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
imagePorts = serverData.DefaultPorts
|
|
||||||
}
|
}
|
||||||
|
|
||||||
imageNameAndVersion := strings.Split(volumeLabels.ImageId, ":")
|
type ServerInfo struct {
|
||||||
|
Id string `json:"Id"`
|
||||||
imageName := imageNameAndVersion[0]
|
OwnerId string `json:"OwnerId"`
|
||||||
imageVersion := imageNameAndVersion[1]
|
DefaultCommand string `json:"DefaultCommand"`
|
||||||
containers, err := con.dockerClient.ContainerList(context.TODO(), container.ListOptions{
|
Image ImageInfo `json:"Image"`
|
||||||
All: true,
|
On bool `json:"On"`
|
||||||
Filters: filters.NewArgs(filters.Arg("label", "type=GAME"), filters.Arg("label", fmt.Sprintf("volume_id=%s", volume.Name))),
|
Nickname string `json:"Nickname"`
|
||||||
})
|
Ports []models.Port `json:"Ports"`
|
||||||
var state bool
|
Domain string `json:"Domain"`
|
||||||
var ports []models.Port
|
|
||||||
if err != nil || len(containers) == 0 {
|
|
||||||
state = false
|
|
||||||
ports = nil
|
|
||||||
} else {
|
|
||||||
container := containers[0]
|
|
||||||
state = container.State == "running"
|
|
||||||
ports = transformContainerPortsToModel(container.Ports)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
serverInfo := models.ServerInfo{
|
type FileBrowserInfo struct {
|
||||||
Id: volume.Name,
|
Id string `json:"Id"`
|
||||||
Image: models.Image{
|
OwnerId string `json:"OwnerId"`
|
||||||
Name: imageName,
|
ConnectedTo ServerInfo `json:"ConnectedTo"`
|
||||||
Version: imageVersion,
|
Url string `json:"Url"`
|
||||||
Ports: imagePorts,
|
|
||||||
},
|
|
||||||
OwnerId: volumeLabels.OwnerId,
|
|
||||||
On: state,
|
|
||||||
Ports: ports,
|
|
||||||
Nickname: serverData.Nickname,
|
|
||||||
DefaultCommand: serverData.DefaultCommand,
|
|
||||||
Domain: DOMAIN,
|
|
||||||
}
|
|
||||||
return &serverInfo, nil
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func (con Connection) getServerInfoFromId(ServerId string) (*models.ServerInfo, error) {
|
|
||||||
volume, err := con.dockerClient.VolumeInspect(context.TODO(), ServerId)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return con.getServerInfo(volume)
|
|
||||||
}
|
|
||||||
|
|
||||||
func transformContainerPortsToModel(ports []types.Port) []models.Port {
|
|
||||||
modelPorts := make([]models.Port, len(ports))
|
|
||||||
for index, port := range ports {
|
|
||||||
modelPorts[index] = models.Port{
|
|
||||||
Number: int(port.PublicPort),
|
|
||||||
Protocol: port.Type,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return modelPorts
|
|
||||||
}
|
|
||||||
|
|
||||||
func convertLabelsToMap(v any) (map[string]string, error) {
|
|
||||||
data, err := json.Marshal(v)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
x := map[string]string{}
|
|
||||||
json.Unmarshal(data, &x)
|
|
||||||
return x, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type CreateServerRequest struct {
|
type CreateServerRequest struct {
|
||||||
@ -187,6 +73,8 @@ func (con Connection) CreateServer(ctx *gin.Context) {
|
|||||||
ctx.AbortWithStatus(500)
|
ctx.AbortWithStatus(500)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
serverClaims := claims.(*auth.AuthClaims)
|
||||||
|
|
||||||
var request CreateServerRequest
|
var request CreateServerRequest
|
||||||
err := json.NewDecoder(ctx.Request.Body).Decode(&request)
|
err := json.NewDecoder(ctx.Request.Body).Decode(&request)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -194,40 +82,34 @@ func (con Connection) CreateServer(ctx *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
imageList, err := con.dockerClient.ImageList(context.TODO(), image.ListOptions{All: true, Filters: filters.NewArgs(filters.Arg("reference", request.ImageId), filters.Arg("label", "type=GAME"))})
|
instanceServer, err := con.InstanceManager.CreateServer(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.AbortWithError(400, err)
|
ctx.AbortWithError(500, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if len(imageList) == 0 {
|
|
||||||
ctx.AbortWithStatusJSON(404, "imageNotFound")
|
imageSegments := strings.Split(request.ImageId, ":")
|
||||||
return
|
|
||||||
}
|
registry := imageSegments[0]
|
||||||
imageSummary := imageList[0]
|
tag := imageSegments[1]
|
||||||
labels, err := convertLabelsToMap(VolumeLabels{OwnerId: claims.(*auth.AuthClaims).Username, ImageId: imageSummary.RepoTags[0], Type: "GAME"})
|
|
||||||
if err != nil {
|
err = con.ServersDbHandler.CreateServer(ctx, dbhandler.Server{
|
||||||
ctx.AbortWithError(400, err)
|
Id: instanceServer.Id,
|
||||||
return
|
Owner: serverClaims.Username,
|
||||||
}
|
Image: &models.Image{
|
||||||
volumeResponse, err := con.dockerClient.VolumeCreate(context.TODO(), volume.CreateOptions{
|
Registry: registry,
|
||||||
Labels: labels,
|
Tag: tag,
|
||||||
|
},
|
||||||
|
Nickname: request.Nickname,
|
||||||
|
Command: request.DefaultCommand,
|
||||||
|
Ports: request.DefaultPorts,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.AbortWithError(500, err)
|
ctx.AbortWithError(500, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
con.databaseConnection.Database("Backend").Collection("Servers").InsertOne(context.TODO(), models.ServerData{
|
|
||||||
Id: volumeResponse.Name,
|
|
||||||
OwnerId: claims.(*auth.AuthClaims).Username,
|
|
||||||
Image: imageSummary.RepoTags[0],
|
|
||||||
VolumeId: volumeResponse.Name,
|
|
||||||
DefaultPorts: request.DefaultPorts,
|
|
||||||
DefaultCommand: request.DefaultCommand,
|
|
||||||
Nickname: request.Nickname,
|
|
||||||
UserPermissions: make(map[string]models.Permission),
|
|
||||||
})
|
|
||||||
|
|
||||||
ctx.JSON(200, volumeResponse.Name)
|
ctx.JSON(200, instanceServer.Id)
|
||||||
}
|
}
|
||||||
|
|
||||||
type PortMappingRequest struct {
|
type PortMappingRequest struct {
|
||||||
@ -242,221 +124,118 @@ type StartServerRequest struct {
|
|||||||
|
|
||||||
func (con Connection) StartServer(ctx *gin.Context) {
|
func (con Connection) StartServer(ctx *gin.Context) {
|
||||||
serverId := ctx.Param("server_id")
|
serverId := ctx.Param("server_id")
|
||||||
claims, exists := ctx.Get("claims")
|
|
||||||
var request StartServerRequest
|
var request StartServerRequest
|
||||||
json.NewDecoder(ctx.Request.Body).Decode(&request)
|
err := json.NewDecoder(ctx.Request.Body).Decode(&request)
|
||||||
|
|
||||||
if !exists {
|
|
||||||
ctx.AbortWithStatus(403)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
serverInfo, err := con.getServerInfoFromId(serverId)
|
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.AbortWithError(500, err)
|
ctx.AbortWithError(500, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if serverInfo.On {
|
instanceServer, err := con.InstanceManager.GetServer(ctx, serverId)
|
||||||
|
if instanceServer.Running {
|
||||||
ctx.Status(200)
|
ctx.Status(200)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
imageId := serverInfo.Image.Name + ":" + serverInfo.Image.Version
|
server, err := con.ServersDbHandler.GetServer(ctx, serverId)
|
||||||
labels := ContainerLabels{
|
|
||||||
OwnerId: claims.(*auth.AuthClaims).Username,
|
|
||||||
ImageId: imageId,
|
|
||||||
VolumeId: serverInfo.Id,
|
|
||||||
Type: "GAME",
|
|
||||||
}
|
|
||||||
|
|
||||||
jsonString, err := json.Marshal(labels)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.AbortWithError(500, err)
|
ctx.AbortWithError(500, err)
|
||||||
return
|
|
||||||
}
|
|
||||||
jsonLabels := make(map[string]string)
|
|
||||||
json.Unmarshal(jsonString, &jsonLabels)
|
|
||||||
|
|
||||||
volumes := make(map[string]struct{})
|
|
||||||
|
|
||||||
image, _, err := con.dockerClient.ImageInspectWithRaw(context.TODO(), imageId)
|
|
||||||
if err != nil {
|
|
||||||
ctx.AbortWithError(500, err)
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var portMapping nat.PortMap = make(nat.PortMap)
|
err = con.InstanceManager.StartServer(
|
||||||
if len(request.Ports) == 0 {
|
ctx,
|
||||||
for _, port := range serverInfo.Image.Ports {
|
instanceServer.Id,
|
||||||
dockerPort, err := nat.NewPort(port.Protocol, fmt.Sprint(port.Number))
|
server.Image.Registry+":"+server.Image.Tag,
|
||||||
if err != nil {
|
server.Command,
|
||||||
ctx.AbortWithError(500, err)
|
server.Ports,
|
||||||
return
|
|
||||||
}
|
|
||||||
portMapping[dockerPort] = []nat.PortBinding{{HostIP: "0.0.0.0"}}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for _, portCouple := range request.Ports {
|
|
||||||
portMapping[nat.Port(fmt.Sprintf("%d/%s", portCouple.Source.Number, portCouple.Source.Protocol))] = []nat.PortBinding{{HostIP: "0.0.0.0", HostPort: fmt.Sprint(portCouple.Destination.Number)}}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
command := request.Command
|
|
||||||
if command == "" {
|
|
||||||
command = serverInfo.DefaultCommand
|
|
||||||
}
|
|
||||||
|
|
||||||
words, err := shellwords.Split(command)
|
|
||||||
if err != nil {
|
|
||||||
ctx.AbortWithError(500, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(words) == 0 {
|
|
||||||
words = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
portSet := make(nat.PortSet)
|
|
||||||
for port := range portMapping {
|
|
||||||
portSet[port] = struct{}{}
|
|
||||||
}
|
|
||||||
|
|
||||||
response, err := con.dockerClient.ContainerCreate(
|
|
||||||
context.TODO(),
|
|
||||||
&container.Config{
|
|
||||||
AttachStdin: true,
|
|
||||||
AttachStdout: true,
|
|
||||||
AttachStderr: true,
|
|
||||||
Tty: true,
|
|
||||||
OpenStdin: true,
|
|
||||||
StdinOnce: false,
|
|
||||||
Image: imageId,
|
|
||||||
Volumes: volumes,
|
|
||||||
Labels: jsonLabels,
|
|
||||||
Cmd: words,
|
|
||||||
ExposedPorts: portSet,
|
|
||||||
},
|
|
||||||
&container.HostConfig{
|
|
||||||
AutoRemove: true,
|
|
||||||
Mounts: []mount.Mount{{Source: serverInfo.Id, Target: image.Config.WorkingDir, Type: "volume"}},
|
|
||||||
PortBindings: portMapping,
|
|
||||||
ConsoleSize: [2]uint{1000, 1000},
|
|
||||||
},
|
|
||||||
&network.NetworkingConfig{},
|
|
||||||
&v1.Platform{},
|
|
||||||
"",
|
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.AbortWithError(500, err)
|
ctx.AbortWithError(500, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if err := con.dockerClient.ContainerStart(ctx, response.ID, container.StartOptions{}); err != nil {
|
|
||||||
ctx.AbortWithError(500, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
UPNPPath, exists := os.LookupEnv("UPNP_PATH")
|
|
||||||
HostIP, hostIPexists := os.LookupEnv("HOST_IP")
|
|
||||||
|
|
||||||
if exists && hostIPexists {
|
ctx.JSON(200, instanceServer.Id)
|
||||||
time.Sleep(time.Millisecond * 100)
|
|
||||||
containerData, err := con.dockerClient.ContainerInspect(context.TODO(), response.ID)
|
|
||||||
if err != nil {
|
|
||||||
ctx.AbortWithError(500, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
fo, err := os.OpenFile(UPNPPath, os.O_WRONLY, os.ModeAppend)
|
|
||||||
if err != nil {
|
|
||||||
ctx.AbortWithError(503, err)
|
|
||||||
con.dockerClient.ContainerStop(context.TODO(), containerData.ID, container.StopOptions{})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer func() {
|
|
||||||
if err := fo.Close(); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
for containerPort, portBindings := range containerData.NetworkSettings.Ports {
|
|
||||||
for _, hostPort := range portBindings {
|
|
||||||
number, proto := hostPort.HostPort, containerPort.Proto()
|
|
||||||
UPNPCommand := fmt.Sprintf("%s|%s|%s|%s\n", HostIP, number, number, strings.ToUpper(proto))
|
|
||||||
_, err := fo.Write([]byte(UPNPCommand))
|
|
||||||
if err != nil {
|
|
||||||
ctx.AbortWithError(500, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
log.Printf("Wrote command \"%s\" to %s", UPNPCommand, UPNPPath)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ctx.JSON(200, response.ID)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (con Connection) GetServers(ctx *gin.Context) {
|
func (con Connection) GetServers(ctx *gin.Context) {
|
||||||
volumes, err := con.dockerClient.VolumeList(
|
instanceServers, err := con.InstanceManager.ListServers(ctx)
|
||||||
context.TODO(),
|
|
||||||
volume.ListOptions{
|
|
||||||
Filters: filters.NewArgs(filters.Arg("label", "type=GAME")),
|
|
||||||
},
|
|
||||||
)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.AbortWithError(500, err)
|
ctx.AbortWithError(500, err)
|
||||||
}
|
return
|
||||||
var servers []models.ServerInfo = make([]models.ServerInfo, 0, len(volumes.Volumes))
|
|
||||||
for _, volume := range volumes.Volumes {
|
|
||||||
serverInfo, err := con.getServerInfo(*volume)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("failed to get server info: %s, %s", volume.Name, err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
servers = append(servers, *serverInfo)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
serverConfigs, err := con.ServersDbHandler.ListServers(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.AbortWithError(500, err)
|
ctx.AbortWithError(500, err)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
serverConfigsMap := make(map[string]dbhandler.Server, len(serverConfigs))
|
||||||
|
|
||||||
|
for _, serverConfig := range serverConfigs {
|
||||||
|
serverConfigsMap[serverConfig.Id] = serverConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
servers := make([]ServerInfo, len(instanceServers))
|
||||||
|
for i, instanceServer := range instanceServers {
|
||||||
|
server := serverConfigsMap[instanceServer.Id]
|
||||||
|
|
||||||
|
var image ImageInfo
|
||||||
|
|
||||||
|
if instanceServer.Running {
|
||||||
|
image = ImageInfo{
|
||||||
|
Name: instanceServer.RunningImage.Registry,
|
||||||
|
Version: instanceServer.RunningImage.Tag,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
image = ImageInfo{
|
||||||
|
Name: server.Image.Registry,
|
||||||
|
Version: server.Image.Tag,
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
servers[i] = ServerInfo{
|
||||||
|
Id: instanceServer.Id,
|
||||||
|
Image: image,
|
||||||
|
OwnerId: server.Owner,
|
||||||
|
DefaultCommand: server.Command,
|
||||||
|
Ports: instanceServer.Ports,
|
||||||
|
On: instanceServer.Running,
|
||||||
|
Nickname: server.Nickname,
|
||||||
|
Domain: instanceServer.Domain,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ctx.JSON(200, servers)
|
ctx.JSON(200, servers)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (con Connection) StopServer(ctx *gin.Context) {
|
func (con Connection) StopServer(ctx *gin.Context) {
|
||||||
serverId := ctx.Param("server_id")
|
serverId := ctx.Param("server_id")
|
||||||
containersList, err := con.dockerClient.ContainerList(context.TODO(), container.ListOptions{
|
err := con.InstanceManager.StopServer(ctx, serverId)
|
||||||
Filters: filters.NewArgs(filters.Arg("label", "volume_id="+serverId), filters.Arg("label", "type=GAME")),
|
|
||||||
})
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.AbortWithError(500, err)
|
ctx.AbortWithError(500, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if len(containersList) == 0 {
|
|
||||||
ctx.Status(200)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, containerData := range containersList {
|
|
||||||
con.dockerClient.ContainerStop(context.TODO(), containerData.ID, container.StopOptions{})
|
|
||||||
}
|
|
||||||
ctx.Status(200)
|
ctx.Status(200)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (con Connection) DeleteServer(ctx *gin.Context) {
|
func (con Connection) DeleteServer(ctx *gin.Context) {
|
||||||
serverId := ctx.Param("server_id")
|
serverId := ctx.Param("server_id")
|
||||||
containers, err := con.dockerClient.ContainerList(context.TODO(), container.ListOptions{All: true, Filters: filters.NewArgs(filters.Arg("label", "volume_id="+serverId))})
|
|
||||||
|
err := con.InstanceManager.DeleteServer(ctx, serverId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.AbortWithError(500, err)
|
ctx.AbortWithError(500, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, containerInstance := range containers {
|
err = con.ServersDbHandler.DeleteServer(ctx, serverId)
|
||||||
err := con.dockerClient.ContainerRemove(context.TODO(), containerInstance.ID, container.RemoveOptions{Force: true})
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.AbortWithError(500, err)
|
ctx.AbortWithError(501, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
con.dockerClient.VolumeRemove(context.TODO(), serverId, false)
|
|
||||||
con.databaseConnection.Database("Backend").Collection("Servers").FindOneAndDelete(context.TODO(), bson.D{{Key: "Id", Value: serverId}})
|
|
||||||
|
|
||||||
ctx.JSON(200, "ok")
|
ctx.JSON(200, "ok")
|
||||||
}
|
}
|
||||||
@ -474,26 +253,21 @@ func (con Connection) RunCommand(ctx *gin.Context) {
|
|||||||
|
|
||||||
serverId := ctx.Param("server_id")
|
serverId := ctx.Param("server_id")
|
||||||
log.Print("Writing command \"", request.Command, "\"")
|
log.Print("Writing command \"", request.Command, "\"")
|
||||||
containers, err := con.dockerClient.ContainerList(context.TODO(), container.ListOptions{Filters: filters.NewArgs(filters.Arg("label", "volume_id="+serverId), filters.Arg("label", "type=GAME"))})
|
|
||||||
|
consolePointer, err := con.InstanceManager.InteractiveTerminal(ctx, serverId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.AbortWithError(500, err)
|
ctx.AbortWithError(500, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, containerData := range containers {
|
console := *consolePointer
|
||||||
hijacked, err := con.dockerClient.ContainerAttach(context.TODO(), containerData.ID, container.AttachOptions{Stream: true, Stdin: true})
|
defer console.Close()
|
||||||
|
|
||||||
|
_, err = console.Write([]byte(request.Command + "\n"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.AbortWithError(500, err)
|
ctx.AbortWithError(500, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer func() { hijacked.Close(); hijacked.CloseWrite() }()
|
|
||||||
number, err := hijacked.Conn.Write([]byte(request.Command + "\n"))
|
|
||||||
log.Print("Wrote ", number, " bytes")
|
|
||||||
if err != nil {
|
|
||||||
ctx.AbortWithError(500, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx.JSON(200, "OK")
|
ctx.JSON(200, "OK")
|
||||||
}
|
}
|
||||||
@ -519,20 +293,12 @@ func (con Connection) AttachServer(ctx *gin.Context) {
|
|||||||
close(containerRead)
|
close(containerRead)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
containers, err := con.dockerClient.ContainerList(context.TODO(), container.ListOptions{Filters: filters.NewArgs(filters.Arg("label", "volume_id="+serverId), filters.Arg("label", "type=GAME"))})
|
hijackedPointer, err := con.InstanceManager.InteractiveTerminal(ctx, serverId)
|
||||||
if err != nil {
|
|
||||||
ctx.AbortWithError(500, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if len(containers) == 0 {
|
|
||||||
ctx.AbortWithStatus(404)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
hijacked, err := con.dockerClient.ContainerAttach(context.TODO(), containers[0].ID, container.AttachOptions{Stream: true, Stdin: true, Stdout: true, Stderr: true, Logs: true})
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.AbortWithError(500, err)
|
ctx.AbortWithError(500, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
hijacked := *hijackedPointer
|
||||||
defer hijacked.Close()
|
defer hijacked.Close()
|
||||||
|
|
||||||
ws, err := upgrader.Upgrade(ctx.Writer, ctx.Request, nil)
|
ws, err := upgrader.Upgrade(ctx.Writer, ctx.Request, nil)
|
||||||
@ -548,10 +314,15 @@ func (con Connection) AttachServer(ctx *gin.Context) {
|
|||||||
if stop {
|
if stop {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
count, err := hijacked.Reader.Read(data)
|
count, err := hijacked.Read(data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
time.Sleep(500)
|
time.Sleep(500)
|
||||||
hijacked, err = con.dockerClient.ContainerAttach(context.TODO(), containers[0].ID, container.AttachOptions{Stream: true, Stdin: true, Stdout: true, Stderr: true, Logs: false})
|
hijackedPointer, err := con.InstanceManager.InteractiveTerminal(ctx, serverId)
|
||||||
|
if err != nil {
|
||||||
|
ctx.AbortWithError(500, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
hijacked = *hijackedPointer
|
||||||
if err != nil {
|
if err != nil {
|
||||||
stop = true
|
stop = true
|
||||||
break
|
break
|
||||||
@ -591,7 +362,7 @@ func (con Connection) AttachServer(ctx *gin.Context) {
|
|||||||
case Command := <-websocketRead:
|
case Command := <-websocketRead:
|
||||||
switch Command.CommandType {
|
switch Command.CommandType {
|
||||||
case "insert":
|
case "insert":
|
||||||
_, err = hijacked.Conn.Write([]byte(Command.Arguments))
|
_, err = hijacked.Write([]byte(Command.Arguments))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Write to docker failed %s", errors.Unwrap(err))
|
log.Printf("Write to docker failed %s", errors.Unwrap(err))
|
||||||
|
|
||||||
@ -616,16 +387,10 @@ func (con Connection) AttachServer(ctx *gin.Context) {
|
|||||||
}
|
}
|
||||||
height := uint(i_height)
|
height := uint(i_height)
|
||||||
|
|
||||||
err2 = con.dockerClient.ContainerResize(context.TODO(), containers[0].ID, container.ResizeOptions{Height: height, Width: width})
|
err2 = con.InstanceManager.ResizeTerminal(ctx, serverId, width, height)
|
||||||
if err2 != nil {
|
if err2 != nil {
|
||||||
log.Printf("Failed to resize container to %dx%d: %s", width, height, err)
|
log.Printf("Failed to resize container to %dx%d: %s", width, height, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// hijacked, err2 = con.dockerClient.ContainerAttach(context.TODO(), containers[0].ID, container.AttachOptions{Stream: true, Stdin: true, Stdout: true, Stderr: true})
|
|
||||||
// if err2 != nil {
|
|
||||||
// log.Printf("Failed to reattach container %s", err)
|
|
||||||
// }
|
|
||||||
|
|
||||||
}
|
}
|
||||||
case data := <-containerRead:
|
case data := <-containerRead:
|
||||||
err := ws.WriteJSON(data)
|
err := ws.WriteJSON(data)
|
||||||
@ -641,33 +406,37 @@ type UpdateServerRequest struct {
|
|||||||
DefaultPorts []models.Port `json:"DefaultPorts"`
|
DefaultPorts []models.Port `json:"DefaultPorts"`
|
||||||
DefaultCommand string `json:"DefaultCommand"`
|
DefaultCommand string `json:"DefaultCommand"`
|
||||||
Nickname string `json:"Nickname"`
|
Nickname string `json:"Nickname"`
|
||||||
UserPermissions map[string][]models.Permission `json:"UserPermissions"`
|
UserPermissions map[string]models.Permission `json:"UserPermissions"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (con Connection) UpdateServer(ctx *gin.Context) {
|
func (con Connection) UpdateServer(ctx *gin.Context) {
|
||||||
serverId := ctx.Param("server_id")
|
serverId := ctx.Param("server_id")
|
||||||
var Request UpdateServerRequest
|
var request UpdateServerRequest
|
||||||
err := json.NewDecoder(ctx.Request.Body).Decode(&Request)
|
err := json.NewDecoder(ctx.Request.Body).Decode(&request)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.AbortWithError(500, err)
|
ctx.AbortWithError(500, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
updateOperation := bson.M{}
|
err = con.ServersDbHandler.UpdateServer(ctx, serverId, dbhandler.ServerUpdateRequest{
|
||||||
if Request.DefaultCommand != "" {
|
Ports: request.DefaultPorts,
|
||||||
updateOperation["DefaultCommand"] = Request.DefaultCommand
|
Nickname: request.Nickname,
|
||||||
}
|
Command: request.DefaultCommand,
|
||||||
if Request.DefaultPorts != nil {
|
})
|
||||||
updateOperation["DefaultPorts"] = Request.DefaultPorts
|
|
||||||
}
|
|
||||||
if Request.Nickname != "" {
|
|
||||||
updateOperation["Nickname"] = Request.Nickname
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = con.databaseConnection.Database("Backend").Collection("Servers").UpdateOne(context.TODO(), bson.D{{Key: "Id", Value: serverId}}, bson.D{{Key: "$set", Value: updateOperation}})
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.AbortWithError(500, err)
|
ctx.AbortWithError(500, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for user, permissions := range request.UserPermissions {
|
||||||
|
err = con.ServerAuthorization.SetPermissions(ctx, user, serverId, permissions)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("failed to change user %s permissions for server %s due to %e", user, serverId, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.JSON(200, "OK")
|
ctx.JSON(200, "OK")
|
||||||
@ -675,115 +444,13 @@ func (con Connection) UpdateServer(ctx *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (con Connection) BrowseServer(ctx *gin.Context) {
|
func (con Connection) BrowseServer(ctx *gin.Context) {
|
||||||
serverID := ctx.Param("server_id")
|
serverId := ctx.Param("server_id")
|
||||||
claims, exists := ctx.Get("claims")
|
browserInfo, err := con.InstanceManager.StartFileBrowser(ctx, serverId)
|
||||||
if !exists {
|
|
||||||
ctx.AbortWithStatus(403)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
serverInfo, err := con.getServerInfoFromId(serverID)
|
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.AbortWithError(500, err)
|
ctx.AbortWithError(500, err)
|
||||||
}
|
}
|
||||||
labelId := serverInfo.Id[:12]
|
|
||||||
browserLabels := make(map[string]string)
|
|
||||||
browserLabels["traefik.enable"] = "true"
|
|
||||||
browserLabels[fmt.Sprintf("traefik.http.routers.%s.rule", labelId)] = fmt.Sprintf("Host(`%s.browsers.%s`)", labelId, DOMAIN)
|
|
||||||
browserLabels[fmt.Sprintf("traefik.http.routers.%s.entrypoints", labelId)] = "websecure"
|
|
||||||
browserLabels[fmt.Sprintf("traefik.http.routers.%s.middlewares", labelId)] = "games@docker"
|
|
||||||
browserLabels[fmt.Sprintf("traefik.http.routers.%s.tls.domains[0].main", labelId)] = fmt.Sprintf("%s.%s", "browsers", DOMAIN)
|
|
||||||
browserLabels[fmt.Sprintf("traefik.http.routers.%s.tls.domains[0].sans", labelId)] = fmt.Sprintf("*.%s.%s", "browsers", DOMAIN)
|
|
||||||
browserLabels[fmt.Sprintf("traefik.http.routers.%s.tls.certresolver", labelId)] = "myresolver"
|
|
||||||
|
|
||||||
containerLabels := ContainerLabels{
|
|
||||||
OwnerId: claims.(*auth.AuthClaims).Username,
|
|
||||||
ImageId: FILE_BROWSER_IMAGE,
|
|
||||||
VolumeId: serverInfo.Id,
|
|
||||||
Type: "FILE_BROWSER",
|
|
||||||
}
|
|
||||||
jsonLabels, err := json.Marshal(containerLabels)
|
|
||||||
if err != nil {
|
|
||||||
ctx.AbortWithError(500, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
err = json.Unmarshal(jsonLabels, &browserLabels)
|
|
||||||
if err != nil {
|
|
||||||
ctx.AbortWithError(500, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
command, err := shellwords.Split("--noauth -r /tmp/data")
|
|
||||||
if err != nil {
|
|
||||||
ctx.AbortWithError(500, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
imageRef := fmt.Sprintf("%s:%s", serverInfo.Image.Name, serverInfo.Image.Version)
|
|
||||||
images, err := con.dockerClient.ImageList(context.TODO(), image.ListOptions{Filters: filters.NewArgs(filters.Arg("label", "type=GAME"), filters.Arg("reference", imageRef))})
|
|
||||||
if err != nil {
|
|
||||||
ctx.AbortWithError(500, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(images) == 0 {
|
|
||||||
ctx.AbortWithError(500, fmt.Errorf("image %s no longer exists", imageRef))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
browserInfo, err := con.getBrowserInfoFromServerId(serverInfo.Id)
|
|
||||||
if err != nil {
|
|
||||||
ctx.AbortWithError(500, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if browserInfo != nil {
|
|
||||||
ctx.JSON(200, browserInfo.Url)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
ContainerResponse, err := con.dockerClient.ContainerCreate(
|
|
||||||
context.TODO(),
|
|
||||||
&container.Config{
|
|
||||||
Cmd: command,
|
|
||||||
Image: FILE_BROWSER_IMAGE,
|
|
||||||
Labels: browserLabels,
|
|
||||||
Tty: true,
|
|
||||||
},
|
|
||||||
&container.HostConfig{
|
|
||||||
Mounts: []mount.Mount{{Source: serverInfo.Id, Target: "/tmp/data", Type: "volume"}},
|
|
||||||
AutoRemove: true,
|
|
||||||
ConsoleSize: [2]uint{1000, 1000},
|
|
||||||
},
|
|
||||||
&network.NetworkingConfig{
|
|
||||||
EndpointsConfig: map[string]*network.EndpointSettings{"test": {
|
|
||||||
NetworkID: "exposed",
|
|
||||||
}},
|
|
||||||
},
|
|
||||||
&v1.Platform{},
|
|
||||||
"",
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
ctx.AbortWithError(500, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
err = con.dockerClient.ContainerStart(context.TODO(), ContainerResponse.ID, container.StartOptions{})
|
|
||||||
if err != nil {
|
|
||||||
ctx.AbortWithError(500, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
browserInfo, err = con.getBrowserInfoFromServerId(serverInfo.Id)
|
|
||||||
if err != nil {
|
|
||||||
ctx.AbortWithError(500, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if browserInfo == nil {
|
|
||||||
ctx.AbortWithError(500, fmt.Errorf("failed to open browser for server %s", serverInfo.Id))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
ctx.JSON(200, browserInfo.Url)
|
ctx.JSON(200, browserInfo.Url)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -793,16 +460,20 @@ func (con Connection) GetServerUserPermissions(ctx *gin.Context) {
|
|||||||
ctx.AbortWithStatus(500)
|
ctx.AbortWithStatus(500)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
authClaims := claims.(*auth.AuthClaims)
|
||||||
|
|
||||||
server_id := ctx.Param("server_id")
|
serverId := ctx.Param("server_id")
|
||||||
if server_id == "" {
|
if serverId == "" {
|
||||||
ctx.AbortWithStatus(500)
|
ctx.AbortWithStatus(500)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
permissions, err := con.ServerAuthorization.GetPermissions(ctx, authClaims.Username, serverId)
|
||||||
|
if err != nil {
|
||||||
|
ctx.AbortWithError(500, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
var serverData models.ServerData
|
ctx.JSON(200, permissions)
|
||||||
con.databaseConnection.Database("Backend").Collection("Servers").FindOne(context.TODO(), bson.D{{Key: "Id", Value: server_id}}).Decode(&serverData)
|
|
||||||
ctx.JSON(200, serverData.UserPermissions[claims.(*auth.AuthClaims).Username])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type SetServerUserPermissionsRequest struct {
|
type SetServerUserPermissionsRequest struct {
|
||||||
@ -811,8 +482,8 @@ type SetServerUserPermissionsRequest struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (con Connection) SetServerUserPermissions(ctx *gin.Context) {
|
func (con Connection) SetServerUserPermissions(ctx *gin.Context) {
|
||||||
server_id := ctx.Param("server_id")
|
serverId := ctx.Param("server_id")
|
||||||
if server_id == "" {
|
if serverId == "" {
|
||||||
ctx.AbortWithStatus(500)
|
ctx.AbortWithStatus(500)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -824,26 +495,52 @@ func (con Connection) SetServerUserPermissions(ctx *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = con.databaseConnection.Database("Backend").Collection("Servers").UpdateOne(context.TODO(), bson.D{{Key: "Id", Value: server_id}}, bson.D{{Key: "$set", Value: bson.D{{Key: fmt.Sprintf("UserPermissions.%s", request.Username), Value: request.Permissions}}}})
|
err = con.ServerAuthorization.SetPermissions(ctx, request.Username, serverId, request.Permissions)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.AbortWithError(500, err)
|
ctx.AbortWithError(500, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.JSON(200, "OK")
|
ctx.JSON(200, "OK")
|
||||||
}
|
}
|
||||||
|
|
||||||
func LoadGroup(group *gin.RouterGroup, mongo_client *mongo.Client, config models.GlobalConfig) {
|
func LoadGroup(group *gin.RouterGroup, config models.GlobalConfig) {
|
||||||
apiClient, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation())
|
|
||||||
|
var instanceManager instancemanager.InstanceManager
|
||||||
|
var serversDbHandler dbhandler.ServersDbHandler
|
||||||
|
var serversAuthorizationHandler dbhandler.ServersAuthorizationDbHandler
|
||||||
|
|
||||||
|
var err error
|
||||||
|
|
||||||
|
if config.InstanceManager.Type == models.DOCKER {
|
||||||
|
instanceManager, err = docker.NewInstanceManager(config.InstanceManager.Docker)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
defer apiClient.Close()
|
}
|
||||||
|
|
||||||
DOMAIN = config.Domain
|
if config.ServersDatabase.Type == models.MONGO {
|
||||||
|
serversDbHandler, err = mongo.NewServersDbHandler(*config.ServersDatabase.Mongo)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
connection := Connection{databaseConnection: mongo_client, dockerClient: apiClient}
|
if config.ServersAuthorizationDatabase.Type == models.MONGO {
|
||||||
authConnection := auth.Connection{DatabaseConnection: mongo_client}
|
serversAuthorizationHandler, err = mongo.NewAuthorizationHandler(*config.ServersAuthorizationDatabase.Mongo)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
connection := Connection{
|
||||||
|
ServersDbHandler: serversDbHandler,
|
||||||
|
ServerAuthorization: serversAuthorizationHandler,
|
||||||
|
InstanceManager: instanceManager,
|
||||||
|
}
|
||||||
|
authConnection := auth.Connection{}
|
||||||
|
|
||||||
group.POST("/:server_id/start", auth.AuthorizedTo(models.Start, authConnection.ServerAuthorized(models.Start)), connection.StartServer)
|
group.POST("/:server_id/start", auth.AuthorizedTo(models.Start, authConnection.ServerAuthorized(models.Start)), connection.StartServer)
|
||||||
group.POST("", auth.AuthorizedTo(models.Create), connection.CreateServer)
|
group.POST("", auth.AuthorizedTo(models.Create), connection.CreateServer)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user