package kubernetes import ( "context" "net" "strings" "git.acooldomain.co/server-manager/backend/instancemanager" "git.acooldomain.co/server-manager/backend/models" servermanagerv1 "git.acooldomain.co/server-manager/kubernetes-operator/api/v1alpha1" "github.com/buildkite/shellwords" "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apiserver/pkg/storage/names" names "k8s.io/apiserver/pkg/storage/names" clientgoscheme "k8s.io/client-go/kubernetes/scheme" "k8s.io/client-go/rest" "sigs.k8s.io/controller-runtime/pkg/client" ) type InstanceManager struct { instancemanager.InstanceManager Config models.KubernetesInstanceManagerConfig client client.Client restCfg *rest.Config coreV1Cli *kubernetes.Clientset } func convertServerManagerImage(image *servermanagerv1.Image) *instancemanager.Image { ports := make([]instancemanager.Port, len(image.Spec.Ports)) for i, port := range image.Spec.Ports { ports[i] = instancemanager.Port{ Number: uint16(port.Port), Protocol: models.PortProtocol(port.Protocol), } } return &instancemanager.Image{ Registry: image.Spec.Name, Tag: image.Spec.Tag, Id: image.Name, Ports: ports, WorkingDir: image.Spec.WorkingDir, Command: strings.Join(image.Spec.Args, " "), } } // General // Read Only func (i *InstanceManager) GetImage(ctx context.Context, imageId string) (*instancemanager.Image, error) { image := &servermanagerv1.Image{} err := i.client.Get(ctx, client.ObjectKey{Name: imageId, Namespace: i.Config.Namespace}, image) if err != nil { return nil, err } return convertServerManagerImage(image), nil } func (i *InstanceManager) ListImages(ctx context.Context) ([]instancemanager.Image, error) { images := &servermanagerv1.ImageList{} err := i.client.List(ctx, images, &client.ListOptions{Namespace: i.Config.Namespace}) if err != nil { return nil, err } imImages := make([]instancemanager.Image, len(images.Items)) for i, image := range images.Items { imImages[i] = *convertServerManagerImage(&image) } return imImages, nil } func (i *InstanceManager) GetServer(ctx context.Context, serverId string) (*instancemanager.Server, error) { serverManager := &servermanagerv1.ServerManager{} err := i.client.Get(ctx, client.ObjectKey{Namespace: i.Config.Namespace, Name: serverId}, serverManager) if err != nil { return nil, err } image, err := i.GetImage(ctx, serverManager.Spec.Server.Image) if err != nil { return nil, err } return &instancemanager.Server{ Id: serverManager.Name, Running: serverManager.Spec.Server.On, RunningCommand: strings.Join(serverManager.Status.Server.Args, " "), RunningImage: &models.Image{ Registry: image.Registry, Tag: image.Tag, Id: image.Id, }, }, nil } func (i *InstanceManager) ListServers(ctx context.Context) ([]instancemanager.Server, error) { serverManagers := &servermanagerv1.ServerManagerList{} err := i.client.List(ctx, serverManagers, &client.ListOptions{Namespace: i.Config.Namespace}) if err != nil { return nil, err } servers := make([]instancemanager.Server, len(serverManagers.Items)) for index, serverManager := range serverManagers.Items { image, err := i.GetImage(ctx, serverManager.Spec.Server.Image) if err != nil { return nil, err } servers[index] = instancemanager.Server{ Id: serverManager.Name, Running: serverManager.Spec.Server.On, RunningCommand: strings.Join(serverManager.Status.Server.Args, " "), RunningImage: &models.Image{ Registry: image.Registry, Tag: image.Tag, Id: image.Id, }, } } return servers, nil } // State Changing func (i *InstanceManager) StartServer(ctx context.Context, serverId string, imageId string, command string, ports []models.Port) error { image := &servermanagerv1.Image{} err := i.client.Get(ctx, client.ObjectKey{Name: imageId, Namespace: i.Config.Namespace}, image) if err != nil { return err } serverManager := &servermanagerv1.ServerManager{} err = i.client.Get(ctx, client.ObjectKey{Name: serverId, Namespace: i.Config.Namespace}, serverManager) if err != nil { return err } serverManager.Spec.Server.Image = image.Name serverManager.Spec.Server.On = true serverManager.Spec.Server.Args, err = shellwords.Split(command) if err != nil { return err } err = i.client.Update(ctx, serverManager) if err != nil { return err } return nil } func (i *InstanceManager) StopServer(ctx context.Context, serverId string) error { serverManager := &servermanagerv1.ServerManager{} err := i.client.Get(ctx, client.ObjectKey{Name: serverId, Namespace: i.Config.Namespace}, serverManager) if err != nil { return err } serverManager.Spec.Server.On = false err = i.client.Update(ctx, serverManager) if err != nil { return err } return nil } func (i *InstanceManager) CreateServer(ctx context.Context) (*instancemanager.Server, error) { name := names.SimpleNameGenerator.GenerateName("") err := i.client.Create(ctx, &servermanagerv1.ServerManager{ ObjectMeta: metav1.ObjectMeta{ Name: name, Namespace: i.Config.Namespace, }, Spec: servermanagerv1.ServerManagerSpec{ Storage: "10Gi", Server: servermanagerv1.ServerSpec{ Image: "", On: false, }, }, }) if err != nil { return nil, err } serverManager := &instancemanager.Server{ Id: name, Running: false, RunningImage: nil, RunningCommand: "", } return serverManager, nil } func (i *InstanceManager) DeleteServer(ctx context.Context, serverId string) error { serverManager := &servermanagerv1.ServerManager{} err := i.client.Get(ctx, client.ObjectKey{Name: serverId, Namespace: i.Config.Namespace}, serverManager) if err != nil { if errors.IsNotFound(err) { return nil } else { return err } } err = i.client.Create(ctx, serverManager) if err != nil { return err } return nil } // Terminal // Status Changing func (i *InstanceManager) InteractiveTerminal(ctx context.Context, serverId string) (*net.Conn, error) { i.client.RESTMapper() return nil, nil } func (i *InstanceManager) ResizeTerminal(ctx context.Context, serverId string, width uint, height uint) error { return nil } // File Browser // Read Only func (i *InstanceManager) GetFileBrowser(ctx context.Context, serverId string) (*models.FileBrowser, error) { return nil, nil } func (i *InstanceManager) ListFileBrowsers(ctx context.Context) ([]models.FileBrowser, error) { return nil, nil } // Status Changing func (i *InstanceManager) StartFileBrowser(ctx context.Context, serverId string) (*models.FileBrowser, error) { return nil, nil } func (i *InstanceManager) StopFileBrowser(ctx context.Context, serverId string) error { return nil } func NewInstanceManager(config models.KubernetesInstanceManagerConfig) (*InstanceManager, error) { c, err := rest.InClusterConfig() if err != nil { return nil, err } scheme := runtime.NewScheme() clientgoscheme.AddToScheme(scheme) servermanagerv1.AddToScheme(scheme) client, err := client.New(c, client.Options{Scheme: scheme}) if err != nil { return nil, err } if err != nil { return nil, err } return &InstanceManager{client: client}, nil }