basic docker impl
This commit is contained in:
parent
2212eee35a
commit
9f19744dce
@ -4,22 +4,43 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"log"
|
"log"
|
||||||
"net"
|
"net"
|
||||||
"strings"
|
"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"
|
||||||
|
"github.com/buildkite/shellwords"
|
||||||
|
"github.com/docker/docker/api/types"
|
||||||
"github.com/docker/docker/api/types/container"
|
"github.com/docker/docker/api/types/container"
|
||||||
"github.com/docker/docker/api/types/image"
|
"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/api/types/volume"
|
||||||
"github.com/docker/docker/client"
|
"github.com/docker/docker/client"
|
||||||
"github.com/docker/go-connections/nat"
|
"github.com/docker/go-connections/nat"
|
||||||
|
v1 "github.com/opencontainers/image-spec/specs-go/v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
type InstanceManager struct {
|
type InstanceManager struct {
|
||||||
instancemanager.InstanceManager
|
instancemanager.InstanceManager
|
||||||
client client.Client
|
client client.Client
|
||||||
|
config models.DockerInstanceManagerConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *InstanceManager) containerList(ctx context.Context, labels ContainerLabels, all bool) ([]types.Container, error) {
|
||||||
|
filters, err := convertLabelsToFilter(labels)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
containers, err := self.client.ContainerList(ctx, container.ListOptions{Filters: *filters, All: all})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return containers, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *InstanceManager) getVolume(ctx context.Context, serverId string) (*volume.Volume, error) {
|
func (self *InstanceManager) getVolume(ctx context.Context, serverId string) (*volume.Volume, error) {
|
||||||
@ -27,14 +48,8 @@ func (self *InstanceManager) getVolume(ctx context.Context, serverId string) (*v
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
var labels VolumeLabels
|
|
||||||
|
|
||||||
rawLabels, err := json.Marshal(volume.Labels)
|
labels, err := convertVolumeLabelsToStruct(volume.Labels)
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = json.Unmarshal(rawLabels, &labels)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -46,30 +61,6 @@ func (self *InstanceManager) getVolume(ctx context.Context, serverId string) (*v
|
|||||||
return &volume, err
|
return &volume, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func convertImagePortsToPorts(rawPorts nat.PortSet) []instancemanager.Port {
|
|
||||||
ports := make([]instancemanager.Port, len(rawPorts))
|
|
||||||
i := 0
|
|
||||||
for imagePort := range rawPorts {
|
|
||||||
portNumber := imagePort.Int()
|
|
||||||
var protocol models.PortProtocol
|
|
||||||
switch imagePort.Proto() {
|
|
||||||
case "TCP":
|
|
||||||
protocol = models.TCP
|
|
||||||
case "UDP":
|
|
||||||
protocol = models.UDP
|
|
||||||
default:
|
|
||||||
log.Default().Println(fmt.Sprintf("Unknown port protocol %s using TCP", imagePort.Proto()))
|
|
||||||
protocol = models.TCP
|
|
||||||
}
|
|
||||||
ports[i] = instancemanager.Port{
|
|
||||||
Number: uint16(portNumber),
|
|
||||||
Protocol: protocol,
|
|
||||||
}
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
return ports
|
|
||||||
}
|
|
||||||
|
|
||||||
// General
|
// General
|
||||||
// Read Only
|
// Read Only
|
||||||
func (self *InstanceManager) ListImages(ctx context.Context) ([]instancemanager.Image, error) {
|
func (self *InstanceManager) ListImages(ctx context.Context) ([]instancemanager.Image, error) {
|
||||||
@ -108,17 +99,12 @@ func (self *InstanceManager) GetServer(ctx context.Context, serverId string) (*i
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
serverContainers, err := self.containerList(
|
||||||
filterArgs, err := convertLabelsToFilter(ContainerLabels{
|
|
||||||
VolumeId: volume.Name,
|
|
||||||
Type: Game,
|
|
||||||
})
|
|
||||||
|
|
||||||
serverContainers, err := self.client.ContainerList(
|
|
||||||
ctx,
|
ctx,
|
||||||
container.ListOptions{
|
ContainerLabels{
|
||||||
Filters: *filterArgs,
|
VolumeId: serverId,
|
||||||
},
|
},
|
||||||
|
true,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -136,17 +122,6 @@ func (self *InstanceManager) GetServer(ctx context.Context, serverId string) (*i
|
|||||||
|
|
||||||
serverContainer := serverContainers[0]
|
serverContainer := serverContainers[0]
|
||||||
|
|
||||||
var containerLabels ContainerLabels
|
|
||||||
|
|
||||||
rawContainerLabels, err := json.Marshal(serverContainer.Labels)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = json.Unmarshal(rawContainerLabels, &containerLabels); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
running := serverContainer.State == "running"
|
running := serverContainer.State == "running"
|
||||||
|
|
||||||
runningCommand := serverContainer.Command
|
runningCommand := serverContainer.Command
|
||||||
@ -167,11 +142,6 @@ func (self *InstanceManager) ListServers(ctx context.Context) ([]instancemanager
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
containerFilters, err := convertLabelsToFilter(ContainerLabels{Type: Game})
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
volumes, err := self.client.VolumeList(ctx, volume.ListOptions{Filters: *volumeFilter})
|
volumes, err := self.client.VolumeList(ctx, volume.ListOptions{Filters: *volumeFilter})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -189,7 +159,7 @@ func (self *InstanceManager) ListServers(ctx context.Context) ([]instancemanager
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
containers, err := self.client.ContainerList(ctx, container.ListOptions{Filters: *containerFilters})
|
containers, err := self.containerList(ctx, ContainerLabels{Type: Game}, false)
|
||||||
|
|
||||||
for _, container := range containers {
|
for _, container := range containers {
|
||||||
rawLabels, err := json.Marshal(container.Labels)
|
rawLabels, err := json.Marshal(container.Labels)
|
||||||
@ -227,52 +197,358 @@ func (self *InstanceManager) ListServers(ctx context.Context) ([]instancemanager
|
|||||||
}
|
}
|
||||||
|
|
||||||
// State Changing
|
// State Changing
|
||||||
func (self *InstanceManager) StartServer(ctx context.Context, serverId string, command string, ports []models.Port) error {
|
func (self *InstanceManager) StartServer(ctx context.Context, serverId string, image instancemanager.Image, command string, ports []models.Port) error {
|
||||||
|
server, err := self.GetServer(ctx, serverId)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if server.Running {
|
||||||
|
return fmt.Errorf("Server %s already running", serverId)
|
||||||
|
}
|
||||||
|
|
||||||
|
imageId := image.Registry + ":" + image.Tag
|
||||||
|
containerLabels := ContainerLabels{
|
||||||
|
VolumeId: server.Id,
|
||||||
|
Type: "GAME",
|
||||||
|
}
|
||||||
|
|
||||||
|
volumes := make(map[string]struct{})
|
||||||
|
|
||||||
|
var portMapping nat.PortMap = make(nat.PortMap)
|
||||||
|
|
||||||
|
if len(ports) == 0 {
|
||||||
|
for _, port := range image.Ports {
|
||||||
|
dockerPort, err := nat.NewPort(string(port.Protocol), fmt.Sprint(port.Number))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
portMapping[dockerPort] = []nat.PortBinding{{HostIP: "0.0.0.0"}}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for _, portCouple := range ports {
|
||||||
|
portMapping[nat.Port(fmt.Sprintf("%d/%s", portCouple.ContainerPort, portCouple.Protocol))] = []nat.PortBinding{{HostIP: "0.0.0.0", HostPort: fmt.Sprint(portCouple.PublicPort)}}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rawImage, _, err := self.client.ImageInspectWithRaw(ctx, imageId)
|
||||||
|
|
||||||
|
if command == "" {
|
||||||
|
command = image.Command
|
||||||
|
}
|
||||||
|
|
||||||
|
words, err := shellwords.Split(command)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(words) == 0 {
|
||||||
|
words = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
portSet := make(nat.PortSet)
|
||||||
|
for port := range portMapping {
|
||||||
|
portSet[port] = struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
labels, err := convertLabelsToMap(containerLabels)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
createdContainer, err := self.client.ContainerCreate(
|
||||||
|
context.TODO(),
|
||||||
|
&container.Config{
|
||||||
|
AttachStdin: true,
|
||||||
|
AttachStdout: true,
|
||||||
|
AttachStderr: true,
|
||||||
|
Tty: true,
|
||||||
|
OpenStdin: true,
|
||||||
|
StdinOnce: false,
|
||||||
|
Image: imageId,
|
||||||
|
Volumes: volumes,
|
||||||
|
Labels: *labels,
|
||||||
|
Cmd: words,
|
||||||
|
ExposedPorts: portSet,
|
||||||
|
},
|
||||||
|
&container.HostConfig{
|
||||||
|
AutoRemove: true,
|
||||||
|
Mounts: []mount.Mount{{Source: server.Id, Target: rawImage.Config.WorkingDir, Type: "volume"}},
|
||||||
|
PortBindings: portMapping,
|
||||||
|
ConsoleSize: [2]uint{1000, 1000},
|
||||||
|
},
|
||||||
|
&network.NetworkingConfig{},
|
||||||
|
&v1.Platform{},
|
||||||
|
"",
|
||||||
|
)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = self.client.ContainerStart(ctx, createdContainer.ID, container.StartOptions{})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
func (self *InstanceManager) StopServer(ctx context.Context, serverId string) error {
|
func (self *InstanceManager) StopServer(ctx context.Context, serverId string) error {
|
||||||
|
runningContainers, err := self.containerList(ctx, ContainerLabels{VolumeId: serverId, Type: Game}, false)
|
||||||
|
|
||||||
|
for _, rawContainer := range runningContainers {
|
||||||
|
err = self.client.ContainerStop(ctx, rawContainer.ID, container.StopOptions{})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *InstanceManager) CreateServer(ctx context.Context, image models.Image) (*instancemanager.Server, error) {
|
func (self *InstanceManager) CreateServer(ctx context.Context) (*instancemanager.Server, error) {
|
||||||
|
labels, err := convertLabelsToMap(VolumeLabels{
|
||||||
|
Type: Game,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
return nil, nil
|
volume, err := self.client.VolumeCreate(ctx, volume.CreateOptions{
|
||||||
|
Labels: *labels,
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &instancemanager.Server{
|
||||||
|
Id: volume.Name,
|
||||||
|
Running: false,
|
||||||
|
RunningImage: nil,
|
||||||
|
RunningCommand: "",
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *InstanceManager) DeleteServer(ctx context.Context, serverId string) error {
|
func (self *InstanceManager) DeleteServer(ctx context.Context, serverId string) error {
|
||||||
|
server, err := self.GetServer(ctx, serverId)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if server.Running {
|
||||||
|
err = self.StopServer(ctx, server.Id)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err = self.StopFileBrowser(ctx, server.Id)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = self.client.VolumeRemove(ctx, server.Id, true)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Terminal
|
// Terminal
|
||||||
|
|
||||||
// Read Only
|
|
||||||
func (self *InstanceManager) GetLogs(ctx context.Context, serverId string) (string, error) {
|
|
||||||
return "", nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Status Changing
|
// Status Changing
|
||||||
func (self *InstanceManager) InteractiveTerminal(ctx context.Context, serverId string) (*net.Conn, error) {
|
func (self *InstanceManager) InteractiveTerminal(ctx context.Context, serverId string) (*net.Conn, error) {
|
||||||
return nil, nil
|
server, err := self.GetServer(ctx, serverId)
|
||||||
}
|
if err != nil {
|
||||||
func (self *InstanceManager) RunCommand(ctx context.Context, serverId string, command string) (string, error) {
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
return "", nil
|
if !server.Running {
|
||||||
|
return nil, fmt.Errorf("Server %s not running", server.Id)
|
||||||
|
}
|
||||||
|
|
||||||
|
rawContainers, err := self.containerList(ctx, ContainerLabels{VolumeId: serverId, Type: Game}, false)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(rawContainers) == 0 {
|
||||||
|
return nil, fmt.Errorf("Server %s not running and wasn't caught", server.Id)
|
||||||
|
}
|
||||||
|
|
||||||
|
rawContainer := rawContainers[0]
|
||||||
|
|
||||||
|
attach, err := self.client.ContainerAttach(ctx,
|
||||||
|
rawContainer.ID,
|
||||||
|
container.AttachOptions{Stream: true,
|
||||||
|
Stdin: true,
|
||||||
|
Stdout: true,
|
||||||
|
Stderr: true,
|
||||||
|
Logs: true,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &attach.Conn, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *InstanceManager) ResizeTerminal(ctx context.Context, serverId string, width uint, height uint) error {
|
||||||
|
containers, err := self.containerList(ctx, ContainerLabels{VolumeId: serverId, Type: Game}, false)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(containers) == 0 {
|
||||||
|
return fmt.Errorf("Server %s not running", serverId)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = self.client.ContainerResize(context.TODO(), containers[0].ID, container.ResizeOptions{Height: height, Width: width})
|
||||||
|
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// File Browser
|
// File Browser
|
||||||
|
|
||||||
// Read Only
|
// Read Only
|
||||||
func (self *InstanceManager) GetFileBrowser(ctx context.Context, serverId string) (*models.FileBrowser, error) {
|
func (self *InstanceManager) GetFileBrowser(ctx context.Context, serverId string) (*models.FileBrowser, error) {
|
||||||
return nil, nil
|
containers, err := self.containerList(ctx, ContainerLabels{VolumeId: serverId, Type: FileBrowser}, false)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(containers) == 0 {
|
||||||
|
return nil, fmt.Errorf("File Browser for server %s is not running", serverId)
|
||||||
|
}
|
||||||
|
|
||||||
|
rawContainer := containers[0]
|
||||||
|
|
||||||
|
containerLabels, err := convertContainerLabelsToStruct(rawContainer.Labels)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &models.FileBrowser{
|
||||||
|
Url: containerLabels.VolumeId[:12] + "." + self.config.BrowsersDomain,
|
||||||
|
Id: rawContainer.ID,
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *InstanceManager) ListFileBrowsers(ctx context.Context) ([]models.FileBrowser, error) {
|
func (self *InstanceManager) ListFileBrowsers(ctx context.Context) ([]models.FileBrowser, error) {
|
||||||
return nil, nil
|
containers, err := self.containerList(ctx, ContainerLabels{Type: FileBrowser}, false)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
fileBrowsers := make([]models.FileBrowser, len(containers))
|
||||||
|
|
||||||
|
for i, rawContainer := range containers {
|
||||||
|
containerLabels, err := convertContainerLabelsToStruct(rawContainer.Labels)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
fileBrowsers[i] = models.FileBrowser{
|
||||||
|
Url: containerLabels.VolumeId[:12] + "." + self.config.BrowsersDomain,
|
||||||
|
Id: rawContainer.ID,
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return fileBrowsers, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Status Changing
|
// Status Changing
|
||||||
func (self *InstanceManager) StartFileBrowser(ctx context.Context, serverId string) (*models.FileBrowser, error) {
|
func (self *InstanceManager) StartFileBrowser(ctx context.Context, serverId string) (*models.FileBrowser, error) {
|
||||||
return nil, nil
|
server, err := self.GetServer(ctx, serverId)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
labelId := serverId[: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, self.config.BrowsersDomain)
|
||||||
|
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", self.config.BrowsersDomain)
|
||||||
|
browserLabels[fmt.Sprintf("traefik.http.routers.%s.tls.domains[0].sans", labelId)] = fmt.Sprintf("*.%s.%s", "browsers", self.config.BrowsersDomain)
|
||||||
|
browserLabels[fmt.Sprintf("traefik.http.routers.%s.tls.certresolver", labelId)] = "myresolver"
|
||||||
|
|
||||||
|
containerConfig, err := convertLabelsToMap(ContainerLabels{VolumeId: serverId, Type: FileBrowser})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for key, value := range *containerConfig {
|
||||||
|
browserLabels[key] = value
|
||||||
|
}
|
||||||
|
|
||||||
|
command := self.config.FileBrowser.Command
|
||||||
|
|
||||||
|
if command == "" {
|
||||||
|
command = "--noauth -r /tmp/data"
|
||||||
|
}
|
||||||
|
|
||||||
|
splitCommand, err := shellwords.Split(command)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
ContainerResponse, err := self.client.ContainerCreate(
|
||||||
|
context.TODO(),
|
||||||
|
&container.Config{
|
||||||
|
Cmd: splitCommand,
|
||||||
|
Image: fmt.Sprintf("%s:%s", self.config.FileBrowser.Image.Registry, self.config.FileBrowser.Image.Tag),
|
||||||
|
Labels: browserLabels,
|
||||||
|
Tty: true,
|
||||||
|
},
|
||||||
|
&container.HostConfig{
|
||||||
|
Mounts: []mount.Mount{{Source: server.Id, Target: "/tmp/data", Type: "volume"}},
|
||||||
|
AutoRemove: true,
|
||||||
|
ConsoleSize: [2]uint{1000, 1000},
|
||||||
|
},
|
||||||
|
&network.NetworkingConfig{
|
||||||
|
EndpointsConfig: map[string]*network.EndpointSettings{"browsers": {
|
||||||
|
NetworkID: self.config.FileBrowser.Network,
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
&v1.Platform{},
|
||||||
|
"",
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = self.client.ContainerStart(context.TODO(), ContainerResponse.ID, container.StartOptions{})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &models.FileBrowser{
|
||||||
|
Url: serverId[:12] + "." + self.config.BrowsersDomain,
|
||||||
|
Id: ContainerResponse.ID,
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *InstanceManager) StopFileBrowser(ctx context.Context, serverId string) error {
|
func (self *InstanceManager) StopFileBrowser(ctx context.Context, serverId string) error {
|
||||||
|
containers, err := self.containerList(ctx, ContainerLabels{Type: FileBrowser}, false)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, rawContainer := range containers {
|
||||||
|
err := self.client.ContainerStop(ctx, rawContainer.ID, container.StopOptions{})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -8,16 +8,18 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type ContainerLabels struct {
|
type ContainerLabels struct {
|
||||||
OwnerId string `json:"user_id,omitempty"`
|
|
||||||
ImageId string `json:"image_id,omitempty"`
|
|
||||||
VolumeId string `json:"volume_id,omitempty"`
|
VolumeId string `json:"volume_id,omitempty"`
|
||||||
Type ContainerType `json:"type,omitempty"`
|
Type ContainerType `json:"type,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type BrowserLabels struct {
|
||||||
|
ContainerLabels
|
||||||
|
UrlRule string
|
||||||
|
Enabled bool
|
||||||
|
}
|
||||||
|
|
||||||
type VolumeLabels struct {
|
type VolumeLabels struct {
|
||||||
OwnerId string `json:"user_id,omitempty"`
|
Type ContainerType `json:"type,omitempty"`
|
||||||
ImageId string `json:"image_id,omitempty"`
|
|
||||||
Type ContainerType `json:"type,omitempty"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type ImageLabels struct {
|
type ImageLabels struct {
|
||||||
|
@ -5,51 +5,54 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
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"
|
||||||
"github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types"
|
||||||
"github.com/docker/docker/api/types/filters"
|
"github.com/docker/docker/api/types/filters"
|
||||||
|
"github.com/docker/go-connections/nat"
|
||||||
)
|
)
|
||||||
|
|
||||||
func convertLabelsToFilter(labels any) (*filters.Args, error) {
|
func convertLabelsToFilter(labels any) (*filters.Args, error) {
|
||||||
args := filters.NewArgs()
|
args := filters.NewArgs()
|
||||||
raw, err := json.Marshal(labels)
|
|
||||||
|
labelMap, err := convertLabelsToMap(labels)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
for key, value := range *labelMap {
|
||||||
var unflattenedMap map[string]any
|
args.Add("label", fmt.Sprintf("%s=%s", key, value))
|
||||||
|
|
||||||
err = json.Unmarshal(raw, &unflattenedMap)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
flatMap := flattenMap(unflattenedMap)
|
|
||||||
|
|
||||||
for key, value := range flatMap {
|
|
||||||
args.Add(key, value)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return &args, nil
|
return &args, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func flattenMap(m map[string]any, previous ...string) map[string]string {
|
func convertLabelsToMap(labels any) (*map[string]string, error) {
|
||||||
flattenedMap := make(map[string]string)
|
|
||||||
for key, value := range m {
|
|
||||||
v := reflect.ValueOf(value)
|
|
||||||
switch v.Kind() {
|
|
||||||
case reflect.Map:
|
|
||||||
inner := flattenMap(v.Interface().(map[string]any), append(previous, key)...)
|
|
||||||
for key, value := range inner {
|
|
||||||
flattenedMap[key] = value
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
raw, err := json.Marshal(labels)
|
||||||
flattenedMap[key] = fmt.Sprintf("%v", value)
|
if err != nil {
|
||||||
}
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return flattenedMap
|
var rawMap map[string]any
|
||||||
|
|
||||||
|
err = json.Unmarshal(raw, &rawMap)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
stringifiedMap := stringifyMap(rawMap)
|
||||||
|
|
||||||
|
return &stringifiedMap, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func stringifyMap(m map[string]any) map[string]string {
|
||||||
|
stringifiedMap := make(map[string]string)
|
||||||
|
for key, value := range m {
|
||||||
|
stringifiedMap[key] = fmt.Sprintf("%v", value)
|
||||||
|
}
|
||||||
|
|
||||||
|
return stringifiedMap
|
||||||
}
|
}
|
||||||
|
|
||||||
func convertContainerPortsToPorts(ports []types.Port) []models.Port {
|
func convertContainerPortsToPorts(ports []types.Port) []models.Port {
|
||||||
@ -87,3 +90,75 @@ func convertContainerImageToImage(image string) models.Image {
|
|||||||
Tag: imageTag,
|
Tag: imageTag,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func convertContainerLabelsToStruct(labels map[string]string) (*ContainerLabels, error) {
|
||||||
|
var containerLabels ContainerLabels
|
||||||
|
|
||||||
|
rawLabels, err := json.Marshal(labels)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = json.Unmarshal(rawLabels, &labels)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &containerLabels, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func convertVolumeLabelsToStruct(labels map[string]string) (*VolumeLabels, error) {
|
||||||
|
var volumeLabels VolumeLabels
|
||||||
|
|
||||||
|
rawLabels, err := json.Marshal(labels)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = json.Unmarshal(rawLabels, &labels)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &volumeLabels, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func convertImageLabelsToStruct(labels map[string]string) (*ImageLabels, error) {
|
||||||
|
var imageLabels ImageLabels
|
||||||
|
|
||||||
|
rawLabels, err := json.Marshal(labels)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = json.Unmarshal(rawLabels, &labels)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &imageLabels, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func convertImagePortsToPorts(rawPorts nat.PortSet) []instancemanager.Port {
|
||||||
|
ports := make([]instancemanager.Port, len(rawPorts))
|
||||||
|
i := 0
|
||||||
|
for imagePort := range rawPorts {
|
||||||
|
portNumber := imagePort.Int()
|
||||||
|
var protocol models.PortProtocol
|
||||||
|
switch imagePort.Proto() {
|
||||||
|
case "TCP":
|
||||||
|
protocol = models.TCP
|
||||||
|
case "UDP":
|
||||||
|
protocol = models.UDP
|
||||||
|
default:
|
||||||
|
log.Default().Println(fmt.Sprintf("Unknown port protocol %s using TCP", imagePort.Proto()))
|
||||||
|
protocol = models.TCP
|
||||||
|
}
|
||||||
|
ports[i] = instancemanager.Port{
|
||||||
|
Number: uint16(portNumber),
|
||||||
|
Protocol: protocol,
|
||||||
|
}
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
return ports
|
||||||
|
}
|
||||||
|
@ -35,20 +35,17 @@ type InstanceManager interface {
|
|||||||
ListServers(ctx context.Context) ([]Server, error)
|
ListServers(ctx context.Context) ([]Server, error)
|
||||||
|
|
||||||
// State Changing
|
// State Changing
|
||||||
StartServer(ctx context.Context, serverId string, command string, ports []models.Port) error
|
StartServer(ctx context.Context, serverId string, image Image, command string, ports []models.Port) error
|
||||||
StopServer(ctx context.Context, serverId string) error
|
StopServer(ctx context.Context, serverId string) error
|
||||||
|
|
||||||
CreateServer(ctx context.Context, image models.Image) (*Server, error)
|
CreateServer(ctx context.Context) (*Server, error)
|
||||||
DeleteServer(ctx context.Context, serverId string) error
|
DeleteServer(ctx context.Context, serverId string) error
|
||||||
|
|
||||||
// Terminal
|
// Terminal
|
||||||
|
|
||||||
// Read Only
|
|
||||||
GetLogs(ctx context.Context, serverId string) (string, error)
|
|
||||||
|
|
||||||
// Status Changing
|
// Status Changing
|
||||||
InteractiveTerminal(ctx context.Context, serverId string) (*net.Conn, error)
|
InteractiveTerminal(ctx context.Context, serverId string) (*net.Conn, error)
|
||||||
RunCommand(ctx context.Context, serverId string, command string) (string, error)
|
ResizeTerminal(ctx context.Context, serverId string, width uint, height uint) error
|
||||||
|
|
||||||
// File Browser
|
// File Browser
|
||||||
|
|
||||||
|
@ -42,7 +42,19 @@ type UsersDatabaseConfig struct {
|
|||||||
Mongo *MongoDBConfig `yaml:"mongo"`
|
Mongo *MongoDBConfig `yaml:"mongo"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ServersInstanceManagerConfig struct {
|
type FileBrowserConfig struct {
|
||||||
|
Image Image `yaml:"image"`
|
||||||
|
Command string `yaml:"command"`
|
||||||
|
Network string `yaml:"network"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type DockerInstanceManagerConfig struct {
|
||||||
|
BrowsersDomain string `yaml:"browsers_domain"`
|
||||||
|
CertificateResolver string `yaml:"certificate_resolver"`
|
||||||
|
FileBrowser FileBrowserConfig `yaml:"file_browser"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type InstanceManagerConfig struct {
|
||||||
Type InstanceManagerType
|
Type InstanceManagerType
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -58,10 +70,11 @@ type ServersAuthorizationDatabaseConfig struct {
|
|||||||
|
|
||||||
type GlobalConfig struct {
|
type GlobalConfig struct {
|
||||||
// Features Configs
|
// Features Configs
|
||||||
Email EmailConfig `yaml:"email"`
|
Email EmailConfig `yaml:"email"`
|
||||||
Domain string `yaml:"domain"`
|
Domain string `yaml:"domain"`
|
||||||
Signing SigningConfig `yaml:"signing"`
|
Signing SigningConfig `yaml:"signing"`
|
||||||
Authentication AuthenticationConfig `yaml:"authentication"`
|
Authentication AuthenticationConfig `yaml:"authentication"`
|
||||||
|
InstanceManager InstanceManagerConfig `yaml:"instance_manager"`
|
||||||
|
|
||||||
// Database Configs
|
// Database Configs
|
||||||
ServersDatabase ServersDatabaseConfig `yaml:"servers_database"`
|
ServersDatabase ServersDatabaseConfig `yaml:"servers_database"`
|
||||||
|
Loading…
x
Reference in New Issue
Block a user