Compare commits

10 Commits

Author SHA1 Message Date
acoolname 54b6631fd0 feat(example): add factorio image example
Build and Push Docker Image / Build image (push) Successful in 1m50s
2026-06-28 22:06:12 +03:00
acoolname 04ad3616c9 chore(filebrowser): migrate from filebrowser to filebrowser quantum
Build and Push Docker Image / Build image (push) Successful in 9m10s
2026-06-28 19:22:35 +03:00
acoolname 7158764d2c fix(browser): change auth method to noauth
Build and Push Docker Image / Build image (push) Successful in 1m45s
2026-06-22 20:12:02 +00:00
acoolname 1f611efed0 fix(browser): set group mount policy
Build and Push Docker Image / Build image (push) Successful in 1m46s
2026-06-22 20:01:03 +00:00
acoolname 935861a107 fix(file-browser): change auth mode to proxy
Build and Push Docker Image / Build image (push) Successful in 1m48s
2026-06-22 19:42:52 +00:00
acoolname 926828a2dc fix(filebrowser): add admin perm to users
Build and Push Docker Image / Build image (push) Successful in 1m49s
2026-01-10 22:14:51 +02:00
acoolname 9ec3ed0f1c fix(filebrowser): fix filebrowser image setter
Build and Push Docker Image / Build image (push) Successful in 6m4s
2025-09-23 13:23:42 +03:00
acoolname e075ff13fa fixed bug where controller did not look at protocol
Build and Push Docker Image / Build image (push) Successful in 1m54s
2025-05-26 16:47:26 +03:00
acoolname 09e9b5dabd added factorio image
Build and Push Docker Image / Build image (push) Successful in 2m0s
2025-05-22 23:07:50 +03:00
acoolname 95a1042c41 fix bug 2025-05-22 23:07:24 +03:00
6 changed files with 225 additions and 27 deletions
@@ -85,6 +85,8 @@ spec:
type: array type: array
tag: tag:
type: string type: string
volume:
type: string
working_dir: working_dir:
type: string type: string
required: required:
+1 -1
View File
@@ -55,7 +55,7 @@ spec:
containers: containers:
- command: - command:
- /manager - /manager
image: git.acooldomain.co/server-manager/kubernetes-operator:v0.0.5 image: git.acooldomain.co/server-manager/kubernetes-operator:v0.0.9
env: env:
- name: CONFIG_PATH - name: CONFIG_PATH
value: /etc/server-manager/config.yaml value: /etc/server-manager/config.yaml
+32
View File
@@ -0,0 +1,32 @@
apiVersion: server-manager.acooldomain.co/v1alpha1
kind: Image
metadata:
labels:
app.kubernetes.io/name: kubernetes-operator
app.kubernetes.io/managed-by: kustomize
name: factorio-2-0-47
spec:
location: git.acooldomain.co/server-manager/factorio:2.0.47
name: factorio
tag: 2.0.47
working_dir: /opt/factorio
volume: /opt/server
command:
- /opt/factorio/bin/x64/factorio
- --port
- "34197"
args:
- --start-server
- saves/default.zip
ports:
- port: 34197
protocol: TCP
init_script:
image: alpine:latest
command:
- /bin/sh
args:
- /bin/sh
- "-c"
- "cp /opt/factorio /opt/server"
+24
View File
@@ -0,0 +1,24 @@
apiVersion: server-manager.acooldomain.co/v1alpha1
kind: Image
metadata:
labels:
app.kubernetes.io/name: kubernetes-operator
app.kubernetes.io/managed-by: kustomize
name: minecraft-paper-1-21-5
spec:
location: git.acooldomain.co/server-manager/minecraft:paper-1.21.5
name: minecraft
tag: paper-1.21.5
working_dir: /opt/server
ports:
- port: 25565
protocol: TCP
init_script:
image: alpine:latest
command:
- /bin/sh
args:
- /bin/sh
- "-c"
- "echo eula=true >> /data/eula.txt"
+66
View File
@@ -0,0 +1,66 @@
package controller
type SourceRule struct {
FolderPath string `yaml:"folderPath"`
Viewable bool `yaml:"viewable"`
}
type SourceConfig struct {
DefaultEnabled bool `yaml:"defaultEnabled"`
Rules []SourceRule `yaml:"rules"`
}
type SourceDefinition struct {
Path string `yaml:"path"`
Config SourceConfig `yaml:"config"`
}
type ServerConfig struct {
Port int `yaml:"port"`
BaseUrl string `yaml:"baseURL"`
Sources []SourceDefinition `yaml:"sources"`
}
type UserDefaultPermissionConfig struct {
Modify bool `yaml:"modify"`
Share bool `yaml:"share"`
Realtime bool `yaml:"realtime"`
Create bool `yaml:"create"`
Delete bool `yaml:"delete"`
Api bool `yaml:"api"`
}
type UserDefaultsConfig struct {
Permissions UserDefaultPermissionConfig `yaml:"permissions"`
}
type AuthPasswordMethodConfig struct {
Enabled bool `yaml:"enabled"`
MinLength int `yaml:"minLength"`
}
type AuthProxyMethodConfig struct {
Enabled bool `yaml:"enabled"`
Header string `yaml:"header"`
}
type AuthMethodsConfig struct {
Password AuthPasswordMethodConfig `yaml:"password"`
Proxy AuthProxyMethodConfig `yaml:"proxy"`
}
type AuthConfig struct {
AdminUsername string `yaml:"adminUsername"`
Methods AuthMethodsConfig `yaml:"methods"`
}
type FrontendConfig struct {
Name string `yaml:"name"`
}
type FileBrowserConfig struct {
Server ServerConfig `yaml:"server"`
Auth AuthConfig `yaml:"auth"`
Frontend FrontendConfig `yaml:"frontend"`
UserDefaults UserDefaultsConfig `yaml:"userDefaults"`
}
+100 -26
View File
@@ -23,6 +23,7 @@ import (
"time" "time"
traefikv3 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/traefikio/v1alpha1" traefikv3 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/traefikio/v1alpha1"
"gopkg.in/yaml.v3"
corev1 "k8s.io/api/core/v1" corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/api/resource" "k8s.io/apimachinery/pkg/api/resource"
@@ -297,7 +298,22 @@ func (r *ServerManagerReconciler) Reconcile(ctx context.Context, req ctrl.Reques
} }
logging.Info("verified server service") logging.Info("verified server service")
browserPod := r.BrowserPod(s, pvc, browserPvc) browserConfigMap, err := r.BrowserConfigMap(s)
if err != nil {
return ctrl.Result{}, err
}
logging.Info("created browser configmap")
var foundBrowserConfigMap corev1.ConfigMap
err = r.Get(ctx, client.ObjectKeyFromObject(browserConfigMap), &foundBrowserConfigMap)
if err != nil {
logging.Info("Failed getting browser configmap", "err", err)
err = r.Create(ctx, browserConfigMap)
return ctrl.Result{Requeue: true}, err
}
logging.Info("verified browser config map")
browserPod := r.BrowserPod(s, pvc, browserPvc, browserConfigMap)
foundBrowser := &corev1.Pod{} foundBrowser := &corev1.Pod{}
err = r.Get(ctx, client.ObjectKeyFromObject(browserPod), foundBrowser) err = r.Get(ctx, client.ObjectKeyFromObject(browserPod), foundBrowser)
if err == nil && !s.Spec.Browser.On { if err == nil && !s.Spec.Browser.On {
@@ -519,16 +535,72 @@ func (r *ServerManagerReconciler) BrowserService(s *servermanagerv1alpha1.Server
return service return service
} }
func (r *ServerManagerReconciler) BrowserPod(s *servermanagerv1alpha1.ServerManager, pvc *corev1.PersistentVolumeClaim, browserPvc *corev1.PersistentVolumeClaim) *corev1.Pod { func (r *ServerManagerReconciler) BrowserConfigMap(s *servermanagerv1alpha1.ServerManager) (*corev1.ConfigMap, error) {
ports := make([]corev1.ContainerPort, len(s.Spec.Server.Ports)) serializedConfig, err := yaml.Marshal(FileBrowserConfig{
Server: ServerConfig{
Port: 80,
BaseUrl: r.GenerateBrowserSubPath(s),
Sources: []SourceDefinition{
{
Path: "/tmp/data",
Config: SourceConfig{
DefaultEnabled: true,
Rules: []SourceRule{
{
FolderPath: "lost+found",
Viewable: false,
},
},
},
},
},
},
Auth: AuthConfig{
AdminUsername: "admin",
Methods: AuthMethodsConfig{
Password: AuthPasswordMethodConfig{
Enabled: false,
},
Proxy: AuthProxyMethodConfig{
Enabled: true,
Header: r.Config.Browser.AuthHeader,
},
},
},
Frontend: FrontendConfig{
Name: "ACoolFileBrowser",
},
UserDefaults: UserDefaultsConfig{
Permissions: UserDefaultPermissionConfig{
Modify: true,
Create: true,
Delete: true,
Api: true,
Share: true,
Realtime: true,
},
},
})
for i, port := range s.Spec.Server.Ports { if err != nil {
ports[i] = corev1.ContainerPort{ return nil, err
ContainerPort: port.Port,
Protocol: port.Protocol,
}
} }
return &corev1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: fmt.Sprintf("%s-browser-config", s.Name),
Namespace: s.Namespace,
Labels: map[string]string{"browser": s.Name},
},
Data: map[string]string{"config.yaml": string(serializedConfig)},
}, nil
}
func (r *ServerManagerReconciler) BrowserPod(s *servermanagerv1alpha1.ServerManager, pvc *corev1.PersistentVolumeClaim, browserPvc *corev1.PersistentVolumeClaim, browserConfigMap *corev1.ConfigMap) *corev1.Pod {
var fsGroupValue int64 = 2000
var runAsUserValue int64 = 1000
policy := corev1.FSGroupChangeOnRootMismatch // Optimizes startup speed
pod := &corev1.Pod{ pod := &corev1.Pod{
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Name: fmt.Sprintf("%s-browser", s.Name), Name: fmt.Sprintf("%s-browser", s.Name),
@@ -536,6 +608,12 @@ func (r *ServerManagerReconciler) BrowserPod(s *servermanagerv1alpha1.ServerMana
Labels: map[string]string{"browser": s.Name}, Labels: map[string]string{"browser": s.Name},
}, },
Spec: corev1.PodSpec{ Spec: corev1.PodSpec{
SecurityContext: &corev1.PodSecurityContext{
FSGroup: &fsGroupValue,
RunAsUser: &runAsUserValue,
RunAsGroup: &fsGroupValue,
FSGroupChangePolicy: &policy,
},
Volumes: []corev1.Volume{ Volumes: []corev1.Volume{
{ {
Name: "volume", Name: "volume",
@@ -553,19 +631,12 @@ func (r *ServerManagerReconciler) BrowserPod(s *servermanagerv1alpha1.ServerMana
}, },
}, },
}, },
}, {Name: "config-volume",
InitContainers: []corev1.Container{ VolumeSource: corev1.VolumeSource{
{ ConfigMap: &corev1.ConfigMapVolumeSource{
Name: "proxy-setter", LocalObjectReference: corev1.LocalObjectReference{
Image: "filebrowser/filebrowser", Name: browserConfigMap.Name,
ImagePullPolicy: corev1.PullIfNotPresent, },
Ports: ports,
Command: []string{"/bin/sh"},
Args: []string{"-c", fmt.Sprintf("rm /tmp/database/filebrowser.db; /filebrowser config init -d /tmp/database/filebrowser.db && /filebrowser config set --auth.method=proxy --auth.header=%s -d /tmp/database/filebrowser.db", r.Config.Browser.AuthHeader)},
VolumeMounts: []corev1.VolumeMount{
{
Name: "browser-volume",
MountPath: "/tmp/database",
}, },
}, },
}, },
@@ -573,10 +644,9 @@ func (r *ServerManagerReconciler) BrowserPod(s *servermanagerv1alpha1.ServerMana
Containers: []corev1.Container{ Containers: []corev1.Container{
{ {
Name: "browser", Name: "browser",
Image: "filebrowser/filebrowser", Image: "ghcr.io/gtsteffaniak/filebrowser",
ImagePullPolicy: corev1.PullIfNotPresent, ImagePullPolicy: corev1.PullIfNotPresent,
Ports: ports, Args: []string{"-c", "/tmp/config/config.yaml"},
Args: []string{"-d", "/tmp/database/filebrowser.db", "-r", "/tmp/data", "-b", r.GenerateBrowserSubPath(s)},
VolumeMounts: []corev1.VolumeMount{ VolumeMounts: []corev1.VolumeMount{
{ {
Name: "volume", Name: "volume",
@@ -586,6 +656,10 @@ func (r *ServerManagerReconciler) BrowserPod(s *servermanagerv1alpha1.ServerMana
Name: "browser-volume", Name: "browser-volume",
MountPath: "/tmp/database", MountPath: "/tmp/database",
}, },
{
Name: "config-volume",
MountPath: "/tmp/config",
},
}, },
}, },
}, },
@@ -599,7 +673,7 @@ func (r *ServerManagerReconciler) BrowserPod(s *servermanagerv1alpha1.ServerMana
func (r *ServerManagerReconciler) ServerService(s *servermanagerv1alpha1.ServerManager) *corev1.Service { func (r *ServerManagerReconciler) ServerService(s *servermanagerv1alpha1.ServerManager) *corev1.Service {
ports := make([]corev1.ServicePort, len(s.Spec.Server.Ports)) ports := make([]corev1.ServicePort, len(s.Spec.Server.Ports))
for i, port := range 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", strings.ToLower(string(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), Protocol: port.Protocol}
} }
service := &corev1.Service{ service := &corev1.Service{
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
@@ -668,7 +742,7 @@ func (r *ServerManagerReconciler) ServerPod(s *servermanagerv1alpha1.ServerManag
Name: "init", Name: "init",
Image: image.Spec.InitScript.Image, Image: image.Spec.InitScript.Image,
ImagePullPolicy: corev1.PullIfNotPresent, ImagePullPolicy: corev1.PullIfNotPresent,
Command: image.Spec.Command, Command: image.Spec.InitScript.Command,
Args: image.Spec.InitScript.Args, Args: image.Spec.InitScript.Args,
VolumeMounts: []corev1.VolumeMount{ VolumeMounts: []corev1.VolumeMount{
{ {