added basic support
This commit is contained in:
parent
d83bc3368a
commit
27d24e5b1d
@ -50,14 +50,13 @@ type ServerSpec struct {
|
||||
}
|
||||
|
||||
type BrowserSpec struct {
|
||||
Image string `json:"image,omitempty"`
|
||||
On bool `json:"on,omitempty"`
|
||||
Port Port `json:"port,omitempty"`
|
||||
}
|
||||
|
||||
// ServerManagerSpec defines the desired state of ServerManager
|
||||
type ServerManagerSpec struct {
|
||||
Id string `json:"id,omitempty"`
|
||||
Storage string `json:"storage,omitempty"`
|
||||
// INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
|
||||
// Important: Run "make" to regenerate code after modifying this file
|
||||
Server ServerSpec `json:"server,omitempty"`
|
||||
|
15
cmd/main.go
15
cmd/main.go
@ -23,6 +23,7 @@ import (
|
||||
|
||||
// Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.)
|
||||
// to ensure that exec-entrypoint and run can make use of them.
|
||||
"gopkg.in/yaml.v3"
|
||||
_ "k8s.io/client-go/plugin/pkg/client/auth"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
@ -37,6 +38,7 @@ import (
|
||||
|
||||
servermanagerv1alpha1 "git.acooldomain.co/server-manager/kubernetes-operator/api/v1alpha1"
|
||||
"git.acooldomain.co/server-manager/kubernetes-operator/internal/controller"
|
||||
traefikv3 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/traefikio/v1alpha1"
|
||||
// +kubebuilder:scaffold:imports
|
||||
)
|
||||
|
||||
@ -49,6 +51,7 @@ func init() {
|
||||
utilruntime.Must(clientgoscheme.AddToScheme(scheme))
|
||||
|
||||
utilruntime.Must(servermanagerv1alpha1.AddToScheme(scheme))
|
||||
utilruntime.Must(traefikv3.AddToScheme(scheme))
|
||||
// +kubebuilder:scaffold:scheme
|
||||
}
|
||||
|
||||
@ -143,12 +146,20 @@ func main() {
|
||||
setupLog.Error(err, "unable to start manager")
|
||||
os.Exit(1)
|
||||
}
|
||||
config := controller.ServerManagerReconcilerConfig{}
|
||||
config := &controller.ServerManagerReconcilerConfig{}
|
||||
configData, err := os.ReadFile("config.yaml")
|
||||
if err != nil {
|
||||
setupLog.Error(err, "unable to read config file")
|
||||
}
|
||||
err = yaml.Unmarshal(configData, config)
|
||||
if err != nil {
|
||||
setupLog.Error(err, "failed to marshal data")
|
||||
}
|
||||
|
||||
if err = (&controller.ServerManagerReconciler{
|
||||
Client: mgr.GetClient(),
|
||||
Scheme: mgr.GetScheme(),
|
||||
Config: config,
|
||||
Config: *config,
|
||||
}).SetupWithManager(mgr); err != nil {
|
||||
setupLog.Error(err, "unable to create controller", "controller", "ServerManager")
|
||||
os.Exit(1)
|
||||
|
2
config.yaml
Normal file
2
config.yaml
Normal file
@ -0,0 +1,2 @@
|
||||
domain_label: "ddns.acooldomain.co/hostname"
|
||||
default_domain: "acooldomain.co"
|
@ -90,6 +90,8 @@ spec:
|
||||
working_dir:
|
||||
type: string
|
||||
type: object
|
||||
storage:
|
||||
type: string
|
||||
type: object
|
||||
status:
|
||||
description: ServerManagerStatus defines the observed state of ServerManager
|
||||
|
@ -6,4 +6,12 @@ metadata:
|
||||
app.kubernetes.io/managed-by: kustomize
|
||||
name: servermanager-sample
|
||||
spec:
|
||||
# TODO(user): Add fields here
|
||||
id: test-serverr
|
||||
storage: 10Gi
|
||||
server:
|
||||
"on": true
|
||||
image: git.acooldomain.co/server-manager/minecraft:paper-1.21.4
|
||||
working_dir: /opt/server
|
||||
ports:
|
||||
- protocol: TCP
|
||||
port: 25565
|
||||
|
@ -19,10 +19,13 @@ package controller
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
traefikv3 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/traefikio/v1alpha1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/util/intstr"
|
||||
@ -34,7 +37,17 @@ import (
|
||||
servermanagerv1alpha1 "git.acooldomain.co/server-manager/kubernetes-operator/api/v1alpha1"
|
||||
)
|
||||
|
||||
type MiddlewareRef struct {
|
||||
Name string `yaml:"name"`
|
||||
Namespace string `yaml:"namespace"`
|
||||
}
|
||||
|
||||
type BrowserConfig struct {
|
||||
Middleware MiddlewareRef `yaml:"middleware"`
|
||||
}
|
||||
|
||||
type ServerManagerReconcilerConfig struct {
|
||||
Browser BrowserConfig `yaml:"browser_middleware"`
|
||||
DomainLabel string `yaml:"domain_label"`
|
||||
DefaultDomain string `yaml:"default_domain"`
|
||||
}
|
||||
@ -84,20 +97,34 @@ func (r *ServerManagerReconciler) Reconcile(ctx context.Context, req ctrl.Reques
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
}
|
||||
logging.Info("verified pvc")
|
||||
|
||||
serverPod := r.ServerPod(s, pvc)
|
||||
if serverPod != nil {
|
||||
found := &corev1.Pod{}
|
||||
err = r.Get(ctx, client.ObjectKey{Namespace: pvc.Namespace, Name: pvc.Name}, found)
|
||||
if err == nil && !s.Spec.Server.On {
|
||||
err = r.Delete(ctx, serverPod)
|
||||
return ctrl.Result{Requeue: true}, err
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
if errors.IsNotFound(err) {
|
||||
if s.Spec.Server.On {
|
||||
err = r.Create(ctx, serverPod)
|
||||
return ctrl.Result{Requeue: true}, err
|
||||
}
|
||||
} else {
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
}
|
||||
|
||||
logging.Info("verified pod")
|
||||
|
||||
if found.Spec.NodeName == "" {
|
||||
logging.Info("waiting for pod to start 2")
|
||||
return ctrl.Result{RequeueAfter: time.Second * 10}, nil
|
||||
}
|
||||
|
||||
domain := r.Config.DefaultDomain
|
||||
if r.Config.DomainLabel != "" {
|
||||
node := &corev1.Node{}
|
||||
@ -113,34 +140,99 @@ func (r *ServerManagerReconciler) Reconcile(ctx context.Context, req ctrl.Reques
|
||||
}
|
||||
}
|
||||
|
||||
logging.Info("got domain", "domain", domain)
|
||||
|
||||
if domain != s.Status.Server.Domain {
|
||||
s.Status.Server.Domain = domain
|
||||
err = r.Update(ctx, s)
|
||||
logging.Info("updating ServerManager object", "NewDomain", domain, "OldDomain", s.Status.Server.Domain)
|
||||
err = r.Status().Update(ctx, s)
|
||||
logging.Info(fmt.Sprintf("%#v", err))
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
service := r.ServerService(s)
|
||||
found_service := &corev1.Service{}
|
||||
err = r.Get(ctx, client.ObjectKeyFromObject(service), found_service)
|
||||
foundService := &corev1.Service{}
|
||||
err = r.Get(ctx, client.ObjectKeyFromObject(service), foundService)
|
||||
if err == nil && !s.Spec.Server.On {
|
||||
err = r.Delete(ctx, service)
|
||||
return ctrl.Result{Requeue: true}, err
|
||||
}
|
||||
if err != nil {
|
||||
if !errors.IsNotFound(err) {
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
if s.Spec.Server.On {
|
||||
err = r.Create(ctx, service)
|
||||
if err != nil {
|
||||
return ctrl.Result{}, err
|
||||
return ctrl.Result{Requeue: true}, err
|
||||
}
|
||||
}
|
||||
logging.Info(fmt.Sprintf("verified service %#v", foundService))
|
||||
|
||||
return ctrl.Result{}, nil
|
||||
}
|
||||
|
||||
func (r *ServerManagerReconciler) BrowserPod(s *servermanagerv1alpha1.ServerManager, pvc *corev1.PersistentVolumeClaim) *corev1.Pod {
|
||||
ports := make([]corev1.ContainerPort, len(s.Spec.Server.Ports))
|
||||
|
||||
for i, port := range s.Spec.Server.Ports {
|
||||
ports[i] = corev1.ContainerPort{
|
||||
ContainerPort: port.Port,
|
||||
Protocol: port.Protocol,
|
||||
}
|
||||
}
|
||||
|
||||
return ctrl.Result{}, nil
|
||||
pod := &corev1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: fmt.Sprintf("%s-browser", s.Name),
|
||||
Namespace: s.Namespace,
|
||||
Labels: map[string]string{"server": s.Name},
|
||||
},
|
||||
Spec: corev1.PodSpec{
|
||||
Volumes: []corev1.Volume{
|
||||
{
|
||||
Name: "volume",
|
||||
VolumeSource: corev1.VolumeSource{
|
||||
PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{
|
||||
ClaimName: pvc.Name,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
InitContainers: []corev1.Container{
|
||||
{
|
||||
Name: "proxy-setter",
|
||||
Image: "filebrowser/filebrowser",
|
||||
ImagePullPolicy: corev1.PullAlways,
|
||||
WorkingDir: s.Spec.Server.WorkingDir,
|
||||
Ports: ports,
|
||||
Args: []string{},
|
||||
},
|
||||
},
|
||||
Containers: []corev1.Container{
|
||||
{
|
||||
Name: "browser",
|
||||
Image: "filebrowser/filebrowser",
|
||||
ImagePullPolicy: corev1.PullAlways,
|
||||
Ports: ports,
|
||||
Args: []string{},
|
||||
VolumeMounts: []corev1.VolumeMount{{
|
||||
Name: "volume",
|
||||
MountPath: s.Spec.Server.WorkingDir,
|
||||
}},
|
||||
Stdin: true,
|
||||
TTY: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
controllerutil.SetControllerReference(s, pod, r.Scheme)
|
||||
return pod
|
||||
}
|
||||
|
||||
func (r *ServerManagerReconciler) ServerService(s *servermanagerv1alpha1.ServerManager) *corev1.Service {
|
||||
ports := make([]corev1.ServicePort, len(s.Spec.Server.Ports))
|
||||
for i, port := range s.Spec.Server.Ports {
|
||||
ports[i] = corev1.ServicePort{NodePort: 0, Port: port.Port, TargetPort: intstr.FromInt32(port.Port), Name: fmt.Sprintf("%s-%d", port.Protocol, port.Port)}
|
||||
ports[i] = corev1.ServicePort{NodePort: 0, Port: port.Port, TargetPort: intstr.FromInt32(port.Port), Name: fmt.Sprintf("%s-%d", strings.ToLower(string(port.Protocol)), port.Port)}
|
||||
}
|
||||
service := &corev1.Service{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
@ -148,9 +240,10 @@ func (r *ServerManagerReconciler) ServerService(s *servermanagerv1alpha1.ServerM
|
||||
Namespace: s.Namespace,
|
||||
},
|
||||
Spec: corev1.ServiceSpec{
|
||||
Type: corev1.ServiceTypeNodePort,
|
||||
Ports: ports,
|
||||
Selector: map[string]string{
|
||||
"server": s.Spec.Id,
|
||||
"server": s.Name,
|
||||
},
|
||||
},
|
||||
}
|
||||
@ -167,6 +260,9 @@ func (r *ServerManagerReconciler) ServerPvc(s *servermanagerv1alpha1.ServerManag
|
||||
|
||||
Spec: corev1.PersistentVolumeClaimSpec{
|
||||
AccessModes: []corev1.PersistentVolumeAccessMode{corev1.ReadWriteMany},
|
||||
Resources: corev1.VolumeResourceRequirements{
|
||||
Requests: corev1.ResourceList{corev1.ResourceStorage: resource.MustParse(s.Spec.Storage)},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@ -176,9 +272,6 @@ func (r *ServerManagerReconciler) ServerPvc(s *servermanagerv1alpha1.ServerManag
|
||||
}
|
||||
|
||||
func (r *ServerManagerReconciler) ServerPod(s *servermanagerv1alpha1.ServerManager, pvc *corev1.PersistentVolumeClaim) *corev1.Pod {
|
||||
if !s.Spec.Server.On {
|
||||
return nil
|
||||
}
|
||||
ports := make([]corev1.ContainerPort, len(s.Spec.Server.Ports))
|
||||
|
||||
for i, port := range s.Spec.Server.Ports {
|
||||
@ -192,6 +285,7 @@ func (r *ServerManagerReconciler) ServerPod(s *servermanagerv1alpha1.ServerManag
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: s.Name,
|
||||
Namespace: s.Namespace,
|
||||
Labels: map[string]string{"server": s.Name},
|
||||
},
|
||||
Spec: corev1.PodSpec{
|
||||
Volumes: []corev1.Volume{
|
||||
@ -208,6 +302,7 @@ func (r *ServerManagerReconciler) ServerPod(s *servermanagerv1alpha1.ServerManag
|
||||
{
|
||||
Name: "server",
|
||||
Image: s.Spec.Server.Image,
|
||||
ImagePullPolicy: corev1.PullAlways,
|
||||
Command: s.Spec.Server.Command,
|
||||
Args: s.Spec.Server.Args,
|
||||
WorkingDir: s.Spec.Server.WorkingDir,
|
||||
|
Loading…
x
Reference in New Issue
Block a user