feat: add Go Kubernetes operator

This commit is contained in:
2026-08-13 02:19:14 +07:00
parent 31a2b8ef28
commit da6eafcae6
30 changed files with 3066 additions and 0 deletions
+51
View File
@@ -0,0 +1,51 @@
package v1alpha1
const (
Domain = "minikura.kirameki.cafe"
LabelPrefix = Domain
LabelServerType = LabelPrefix + "/server-type"
LabelServerID = LabelPrefix + "/server-id"
LabelProxyID = LabelPrefix + "/proxy-id"
LabelManagedBy = LabelPrefix + "/managed-by"
ManagerName = "minikura-operator"
PhasePending = "Pending"
PhaseRunning = "Running"
PhaseFailed = "Failed"
ConditionReady = "Ready"
)
type EnvVar struct {
Name string `json:"name"`
// +optional
Value string `json:"value,omitempty"`
}
// +kubebuilder:validation:Enum=ClusterIP;NodePort;LoadBalancer
type ServiceExposure string
const (
ExposureClusterIP ServiceExposure = "ClusterIP"
ExposureNodePort ServiceExposure = "NodePort"
ExposureLoadBalancer ServiceExposure = "LoadBalancer"
)
type Resources struct {
// +kubebuilder:validation:Minimum=256
// +kubebuilder:default=2048
MemoryLimitMB int32 `json:"memoryLimitMB,omitempty"`
// +optional
// +kubebuilder:validation:Minimum=256
MemoryRequestMB int32 `json:"memoryRequestMB,omitempty"`
// +kubebuilder:default="500m"
CPURequest string `json:"cpuRequest,omitempty"`
// +kubebuilder:default="2"
CPULimit string `json:"cpuLimit,omitempty"`
}
@@ -0,0 +1,16 @@
// +kubebuilder:object:generate=true
// +groupName=minikura.kirameki.cafe
package v1alpha1
import (
"k8s.io/apimachinery/pkg/runtime/schema"
"sigs.k8s.io/controller-runtime/pkg/scheme"
)
var (
GroupVersion = schema.GroupVersion{Group: "minikura.kirameki.cafe", Version: "v1alpha1"}
SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion}
AddToScheme = SchemeBuilder.AddToScheme
)
@@ -0,0 +1,164 @@
package v1alpha1
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// +kubebuilder:validation:Enum=STATEFUL;STATELESS
type ServerKind string
const (
ServerStateful ServerKind = "STATEFUL"
ServerStateless ServerKind = "STATELESS"
)
// +kubebuilder:validation:Enum=VANILLA;PAPER;SPIGOT;PURPUR;FABRIC;FORGE;FOLIA
type JarType string
// +kubebuilder:validation:Enum=PEACEFUL;EASY;NORMAL;HARD
type Difficulty string
// +kubebuilder:validation:Enum=SURVIVAL;CREATIVE;ADVENTURE;SPECTATOR
type GameMode string
type MinecraftProperties struct {
// +kubebuilder:default=EASY
Difficulty Difficulty `json:"difficulty,omitempty"`
// +kubebuilder:default=SURVIVAL
GameMode GameMode `json:"gameMode,omitempty"`
// +kubebuilder:validation:Minimum=1
// +kubebuilder:default=20
MaxPlayers int32 `json:"maxPlayers,omitempty"`
// +kubebuilder:default=true
PVP *bool `json:"pvp,omitempty"`
// +kubebuilder:default=true
OnlineMode *bool `json:"onlineMode,omitempty"`
// +optional
MOTD string `json:"motd,omitempty"`
// +optional
LevelSeed string `json:"levelSeed,omitempty"`
// +optional
LevelType string `json:"levelType,omitempty"`
}
type JVMOptions struct {
// +optional
Opts string `json:"opts,omitempty"`
// +optional
UseAikarFlags bool `json:"useAikarFlags,omitempty"`
// +optional
UseMeowIceFlags bool `json:"useMeowIceFlags,omitempty"`
// +kubebuilder:validation:Minimum=1
// +kubebuilder:validation:Maximum=100
// +kubebuilder:default=80
HeapPercent int32 `json:"heapPercent,omitempty"`
}
type MinecraftServerSpec struct {
Type ServerKind `json:"type"`
// +optional
Description string `json:"description,omitempty"`
// +kubebuilder:validation:Minimum=1
// +kubebuilder:validation:Maximum=65535
// +kubebuilder:default=25565
ListenPort int32 `json:"listenPort,omitempty"`
// +kubebuilder:default=ClusterIP
ServiceType ServiceExposure `json:"serviceType,omitempty"`
// +optional
// +kubebuilder:validation:Minimum=30000
// +kubebuilder:validation:Maximum=32767
NodePort int32 `json:"nodePort,omitempty"`
// +kubebuilder:default=VANILLA
JarType JarType `json:"jarType,omitempty"`
// +kubebuilder:default="LATEST"
MinecraftVersion string `json:"minecraftVersion,omitempty"`
// +optional
Resources Resources `json:"resources,omitempty"`
// +optional
JVM JVMOptions `json:"jvm,omitempty"`
// +optional
Properties MinecraftProperties `json:"properties,omitempty"`
// +optional
Env []EnvVar `json:"env,omitempty"`
// +optional
APIKeySecretRef string `json:"apiKeySecretRef,omitempty"`
// +kubebuilder:default="1Gi"
StorageSize string `json:"storageSize,omitempty"`
}
type MinecraftServerStatus struct {
// +optional
Phase string `json:"phase,omitempty"`
// +optional
Message string `json:"message,omitempty"`
// +optional
ReadyReplicas int32 `json:"readyReplicas,omitempty"`
// +optional
Replicas int32 `json:"replicas,omitempty"`
// +optional
Endpoint string `json:"endpoint,omitempty"`
// +optional
ObservedGeneration int64 `json:"observedGeneration,omitempty"`
// +optional
// +patchMergeKey=type
// +patchStrategy=merge
// +listType=map
// +listMapKey=type
Conditions []metav1.Condition `json:"conditions,omitempty"`
}
// +kubebuilder:object:root=true
// +kubebuilder:subresource:status
// +kubebuilder:resource:shortName=mcs
// +kubebuilder:printcolumn:name="Type",type=string,JSONPath=`.spec.type`
// +kubebuilder:printcolumn:name="Version",type=string,JSONPath=`.spec.minecraftVersion`
// +kubebuilder:printcolumn:name="Phase",type=string,JSONPath=`.status.phase`
// +kubebuilder:printcolumn:name="Ready",type=string,JSONPath=`.status.readyReplicas`
// +kubebuilder:printcolumn:name="Endpoint",type=string,JSONPath=`.status.endpoint`
// +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp`
type MinecraftServer struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec MinecraftServerSpec `json:"spec,omitempty"`
Status MinecraftServerStatus `json:"status,omitempty"`
}
// +kubebuilder:object:root=true
type MinecraftServerList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []MinecraftServer `json:"items"`
}
func init() {
SchemeBuilder.Register(&MinecraftServer{}, &MinecraftServerList{})
}
@@ -0,0 +1,111 @@
package v1alpha1
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// +kubebuilder:validation:Enum=VELOCITY;BUNGEECORD
type ProxyKind string
const (
ProxyVelocity ProxyKind = "VELOCITY"
ProxyBungeeCord ProxyKind = "BUNGEECORD"
)
type ReverseProxyServerSpec struct {
Type ProxyKind `json:"type"`
// +optional
Description string `json:"description,omitempty"`
ExternalAddress string `json:"externalAddress"`
// +kubebuilder:validation:Minimum=1
// +kubebuilder:validation:Maximum=65535
ExternalPort int32 `json:"externalPort"`
// +kubebuilder:validation:Minimum=1
// +kubebuilder:validation:Maximum=65535
// +kubebuilder:default=25565
ListenPort int32 `json:"listenPort,omitempty"`
// +kubebuilder:default=LoadBalancer
ServiceType ServiceExposure `json:"serviceType,omitempty"`
// +optional
// +kubebuilder:validation:Minimum=30000
// +kubebuilder:validation:Maximum=32767
NodePort int32 `json:"nodePort,omitempty"`
// +optional
Resources Resources `json:"resources,omitempty"`
// +optional
JVM JVMOptions `json:"jvm,omitempty"`
// +optional
Env []EnvVar `json:"env,omitempty"`
// +optional
APIKeySecretRef string `json:"apiKeySecretRef,omitempty"`
// +optional
BackendSelector *metav1.LabelSelector `json:"backendSelector,omitempty"`
}
type ReverseProxyServerStatus struct {
// +optional
Phase string `json:"phase,omitempty"`
// +optional
Message string `json:"message,omitempty"`
// +optional
ReadyReplicas int32 `json:"readyReplicas,omitempty"`
// +optional
Replicas int32 `json:"replicas,omitempty"`
// +optional
Endpoint string `json:"endpoint,omitempty"`
// +optional
Backends []string `json:"backends,omitempty"`
// +optional
ObservedGeneration int64 `json:"observedGeneration,omitempty"`
// +optional
// +patchMergeKey=type
// +patchStrategy=merge
// +listType=map
// +listMapKey=type
Conditions []metav1.Condition `json:"conditions,omitempty"`
}
// +kubebuilder:object:root=true
// +kubebuilder:subresource:status
// +kubebuilder:resource:shortName=rps
// +kubebuilder:printcolumn:name="Type",type=string,JSONPath=`.spec.type`
// +kubebuilder:printcolumn:name="Address",type=string,JSONPath=`.spec.externalAddress`
// +kubebuilder:printcolumn:name="Phase",type=string,JSONPath=`.status.phase`
// +kubebuilder:printcolumn:name="Ready",type=string,JSONPath=`.status.readyReplicas`
// +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp`
type ReverseProxyServer struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec ReverseProxyServerSpec `json:"spec,omitempty"`
Status ReverseProxyServerStatus `json:"status,omitempty"`
}
// +kubebuilder:object:root=true
type ReverseProxyServerList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []ReverseProxyServer `json:"items"`
}
func init() {
SchemeBuilder.Register(&ReverseProxyServer{}, &ReverseProxyServerList{})
}
@@ -0,0 +1,297 @@
//go:build !ignore_autogenerated
// Code generated by controller-gen. DO NOT EDIT.
package v1alpha1
import (
"k8s.io/apimachinery/pkg/apis/meta/v1"
runtime "k8s.io/apimachinery/pkg/runtime"
)
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *EnvVar) DeepCopyInto(out *EnvVar) {
*out = *in
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EnvVar.
func (in *EnvVar) DeepCopy() *EnvVar {
if in == nil {
return nil
}
out := new(EnvVar)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *JVMOptions) DeepCopyInto(out *JVMOptions) {
*out = *in
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new JVMOptions.
func (in *JVMOptions) DeepCopy() *JVMOptions {
if in == nil {
return nil
}
out := new(JVMOptions)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *MinecraftProperties) DeepCopyInto(out *MinecraftProperties) {
*out = *in
if in.PVP != nil {
in, out := &in.PVP, &out.PVP
*out = new(bool)
**out = **in
}
if in.OnlineMode != nil {
in, out := &in.OnlineMode, &out.OnlineMode
*out = new(bool)
**out = **in
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MinecraftProperties.
func (in *MinecraftProperties) DeepCopy() *MinecraftProperties {
if in == nil {
return nil
}
out := new(MinecraftProperties)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *MinecraftServer) DeepCopyInto(out *MinecraftServer) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
in.Spec.DeepCopyInto(&out.Spec)
in.Status.DeepCopyInto(&out.Status)
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MinecraftServer.
func (in *MinecraftServer) DeepCopy() *MinecraftServer {
if in == nil {
return nil
}
out := new(MinecraftServer)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *MinecraftServer) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *MinecraftServerList) DeepCopyInto(out *MinecraftServerList) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ListMeta.DeepCopyInto(&out.ListMeta)
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]MinecraftServer, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MinecraftServerList.
func (in *MinecraftServerList) DeepCopy() *MinecraftServerList {
if in == nil {
return nil
}
out := new(MinecraftServerList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *MinecraftServerList) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *MinecraftServerSpec) DeepCopyInto(out *MinecraftServerSpec) {
*out = *in
out.Resources = in.Resources
out.JVM = in.JVM
in.Properties.DeepCopyInto(&out.Properties)
if in.Env != nil {
in, out := &in.Env, &out.Env
*out = make([]EnvVar, len(*in))
copy(*out, *in)
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MinecraftServerSpec.
func (in *MinecraftServerSpec) DeepCopy() *MinecraftServerSpec {
if in == nil {
return nil
}
out := new(MinecraftServerSpec)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *MinecraftServerStatus) DeepCopyInto(out *MinecraftServerStatus) {
*out = *in
if in.Conditions != nil {
in, out := &in.Conditions, &out.Conditions
*out = make([]v1.Condition, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MinecraftServerStatus.
func (in *MinecraftServerStatus) DeepCopy() *MinecraftServerStatus {
if in == nil {
return nil
}
out := new(MinecraftServerStatus)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Resources) DeepCopyInto(out *Resources) {
*out = *in
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Resources.
func (in *Resources) DeepCopy() *Resources {
if in == nil {
return nil
}
out := new(Resources)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ReverseProxyServer) DeepCopyInto(out *ReverseProxyServer) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
in.Spec.DeepCopyInto(&out.Spec)
in.Status.DeepCopyInto(&out.Status)
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ReverseProxyServer.
func (in *ReverseProxyServer) DeepCopy() *ReverseProxyServer {
if in == nil {
return nil
}
out := new(ReverseProxyServer)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *ReverseProxyServer) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ReverseProxyServerList) DeepCopyInto(out *ReverseProxyServerList) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ListMeta.DeepCopyInto(&out.ListMeta)
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]ReverseProxyServer, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ReverseProxyServerList.
func (in *ReverseProxyServerList) DeepCopy() *ReverseProxyServerList {
if in == nil {
return nil
}
out := new(ReverseProxyServerList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *ReverseProxyServerList) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ReverseProxyServerSpec) DeepCopyInto(out *ReverseProxyServerSpec) {
*out = *in
out.Resources = in.Resources
out.JVM = in.JVM
if in.Env != nil {
in, out := &in.Env, &out.Env
*out = make([]EnvVar, len(*in))
copy(*out, *in)
}
if in.BackendSelector != nil {
in, out := &in.BackendSelector, &out.BackendSelector
*out = new(v1.LabelSelector)
(*in).DeepCopyInto(*out)
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ReverseProxyServerSpec.
func (in *ReverseProxyServerSpec) DeepCopy() *ReverseProxyServerSpec {
if in == nil {
return nil
}
out := new(ReverseProxyServerSpec)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ReverseProxyServerStatus) DeepCopyInto(out *ReverseProxyServerStatus) {
*out = *in
if in.Backends != nil {
in, out := &in.Backends, &out.Backends
*out = make([]string, len(*in))
copy(*out, *in)
}
if in.Conditions != nil {
in, out := &in.Conditions, &out.Conditions
*out = make([]v1.Condition, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ReverseProxyServerStatus.
func (in *ReverseProxyServerStatus) DeepCopy() *ReverseProxyServerStatus {
if in == nil {
return nil
}
out := new(ReverseProxyServerStatus)
in.DeepCopyInto(out)
return out
}