backend/instance_manager/docker/instance_manager.go

318 lines
7.5 KiB
Go

package docker
import (
"context"
"encoding/json"
"fmt"
"log"
"net"
"reflect"
"strings"
instancemanager "git.acooldomain.co/server-manager/backend-kubernetes-go/instance_manager"
"git.acooldomain.co/server-manager/backend-kubernetes-go/models"
"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/volume"
"github.com/docker/docker/client"
)
type InstanceManager struct {
instancemanager.InstanceManager
client client.Client
}
func flattenMap(m map[string]any, previous ...string) map[string]string {
var flattenedMap 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:
flattenedMap[key] = fmt.Sprintf("%v", value)
}
}
return flattenedMap
}
func convertLabelsToFilter(labels any) (*filters.Args, error) {
args := filters.NewArgs()
raw, err := json.Marshal(labels)
if err != nil {
return nil, err
}
var unflattenedMap map[string]any
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
}
func convertContainerPortsToPorts(ports []types.Port) []models.Port {
containerPorts := make([]models.Port, len(ports))
logger := log.Default()
for i, port := range ports {
var portProtocol models.PortProtocol
switch port.Type {
case "TCP":
portProtocol = models.TCP
case "UDP":
portProtocol = models.UDP
default:
logger.Println(fmt.Sprintf("Unkown Port Protocol %s assuming TCP", port.Type))
portProtocol = models.TCP
}
containerPorts[i] = models.Port{
PublicPort: port.PublicPort,
ContainerPort: port.PrivatePort,
Protocol: portProtocol,
}
}
return containerPorts
}
func convertContainerImageToImage(image string) models.Image {
imageSegments := strings.Split(image, ":")
imageRegistry := imageSegments[0]
imageTag := imageSegments[1]
return models.Image{
Registry: imageRegistry,
Tag: imageTag,
}
}
func convertImagePortsToPorts(types.Port) *models.Port {
return nil
}
// General
// Read Only
func (self *InstanceManager) ListImages(ctx context.Context) (*instancemanager.Image, error) {
imageFilters, err := convertLabelsToFilter(ImageLabels{Type: Game})
if err != nil {
return nil, err
}
rawImages, err := self.client.ImageList(ctx, image.ListOptions{
Filters: *imageFilters,
})
images := make([]instancemanager.Image, len(rawImages))
for i, rawImage := range rawImages {
imageInspect, _, err := self.client.ImageInspectWithRaw(ctx, rawImage.ID)
if err != nil {
return nil, err
}
}
return nil, nil
}
func (self *InstanceManager) GetServer(ctx context.Context, serverId string) (*instancemanager.Server, error) {
volume, err := self.client.VolumeInspect(ctx, serverId)
if err != nil {
return nil, err
}
var labels VolumeLabels
rawLabels, err := json.Marshal(volume.Labels)
if err != nil {
return nil, err
}
err = json.Unmarshal(rawLabels, &labels)
if err != nil {
return nil, err
}
if labels.Type != Game {
return nil, fmt.Errorf("Server not found")
}
filterArgs, err := convertLabelsToFilter(ContainerLabels{
VolumeId: volume.Name,
Type: Game,
})
serverContainers, err := self.client.ContainerList(
ctx,
container.ListOptions{
Filters: *filterArgs,
},
)
if err != nil {
return nil, err
}
if len(serverContainers) == 0 {
return &instancemanager.Server{
Id: volume.Name,
Running: false,
RunningCommand: "",
RunningImage: nil,
Ports: nil,
}, nil
}
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"
runningCommand := serverContainer.Command
image := convertContainerImageToImage(serverContainer.Image)
return &instancemanager.Server{
Id: volume.Name,
Running: running,
RunningCommand: runningCommand,
Ports: convertContainerPortsToPorts(serverContainer.Ports),
RunningImage: &image,
}, nil
}
func (self *InstanceManager) ListServers(ctx context.Context) ([]instancemanager.Server, error) {
volumeFilter, err := convertLabelsToFilter(VolumeLabels{Type: Game})
if err != nil {
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})
if err != nil {
return nil, err
}
serverStatus := make(map[string]*instancemanager.Server)
for _, volume := range volumes.Volumes {
serverStatus[volume.Name] = &instancemanager.Server{
Id: volume.Name,
Running: false,
RunningCommand: "",
Ports: nil,
RunningImage: nil,
}
}
containers, err := self.client.ContainerList(ctx, container.ListOptions{Filters: *containerFilters})
for _, container := range containers {
rawLabels, err := json.Marshal(container.Labels)
if err != nil {
return nil, err
}
var containerLabels ContainerLabels
err = json.Unmarshal(rawLabels, &containerLabels)
if err != nil {
return nil, err
}
if container.State != "running" {
continue
}
image := convertContainerImageToImage(container.Image)
serverStatus[containerLabels.VolumeId].Ports = convertContainerPortsToPorts(container.Ports)
serverStatus[containerLabels.VolumeId].Running = true
serverStatus[containerLabels.VolumeId].RunningImage = &image
serverStatus[containerLabels.VolumeId].RunningCommand = container.Command
}
servers := make([]instancemanager.Server, len(serverStatus))
i := 0
for _, value := range serverStatus {
servers[i] = *value
i++
}
return servers, nil
}
// State Changing
func (self *InstanceManager) StartServer(ctx context.Context, serverId string, command string, ports []models.Port) error {
return nil
}
func (self *InstanceManager) StopServer(ctx context.Context, serverId string) error {
return nil
}
func (self *InstanceManager) CreateServer(ctx context.Context, image models.Image) (*instancemanager.Server, error) {
return nil, nil
}
func (self *InstanceManager) DeleteServer(ctx context.Context, serverId string) error {
return nil
}
// Terminal
// Read Only
func (self *InstanceManager) GetLogs(ctx context.Context, serverId string) (string, error) {
return "", nil
}
// Status Changing
func (self *InstanceManager) InteractiveTerminal(ctx context.Context, serverId string) (*net.Conn, error) {
return nil, nil
}
func (self *InstanceManager) RunCommand(ctx context.Context, serverId string, command string) (string, error) {
return "", nil
}
// File Browser
// Read Only
func (self *InstanceManager) GetFileBrowser(ctx context.Context, serverId string) (*models.FileBrowser, error) {
return nil, nil
}
func (self *InstanceManager) ListFileBrowsers(ctx context.Context) ([]models.FileBrowser, error) {
return nil, nil
}
// Status Changing
func (self *InstanceManager) StartFileBrowser(ctx context.Context, serverId string) (*models.FileBrowser, error) {
return nil, nil
}
func (self *InstanceManager) StopFileBrowser(ctx context.Context, serverId string) error {
return nil
}