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
@@ -0,0 +1,269 @@
---
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.21.0
name: minecraftservers.minikura.kirameki.cafe
spec:
group: minikura.kirameki.cafe
names:
kind: MinecraftServer
listKind: MinecraftServerList
plural: minecraftservers
shortNames:
- mcs
singular: minecraftserver
scope: Namespaced
versions:
- additionalPrinterColumns:
- jsonPath: .spec.type
name: Type
type: string
- jsonPath: .spec.minecraftVersion
name: Version
type: string
- jsonPath: .status.phase
name: Phase
type: string
- jsonPath: .status.readyReplicas
name: Ready
type: string
- jsonPath: .status.endpoint
name: Endpoint
type: string
- jsonPath: .metadata.creationTimestamp
name: Age
type: date
name: v1alpha1
schema:
openAPIV3Schema:
properties:
apiVersion:
description: |-
APIVersion defines the versioned schema of this representation of an object.
Servers should convert recognized schemas to the latest internal value, and
may reject unrecognized values.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
type: string
kind:
description: |-
Kind is a string value representing the REST resource this object represents.
Servers may infer this from the endpoint the client submits requests to.
Cannot be updated.
In CamelCase.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
type: string
metadata:
type: object
spec:
properties:
apiKeySecretRef:
type: string
description:
type: string
env:
items:
properties:
name:
type: string
value:
type: string
required:
- name
type: object
type: array
jarType:
default: VANILLA
enum:
- VANILLA
- PAPER
- SPIGOT
- PURPUR
- FABRIC
- FORGE
- FOLIA
type: string
jvm:
properties:
heapPercent:
default: 80
format: int32
maximum: 100
minimum: 1
type: integer
opts:
type: string
useAikarFlags:
type: boolean
useMeowIceFlags:
type: boolean
type: object
listenPort:
default: 25565
format: int32
maximum: 65535
minimum: 1
type: integer
minecraftVersion:
default: LATEST
type: string
nodePort:
format: int32
maximum: 32767
minimum: 30000
type: integer
properties:
properties:
difficulty:
default: EASY
enum:
- PEACEFUL
- EASY
- NORMAL
- HARD
type: string
gameMode:
default: SURVIVAL
enum:
- SURVIVAL
- CREATIVE
- ADVENTURE
- SPECTATOR
type: string
levelSeed:
type: string
levelType:
type: string
maxPlayers:
default: 20
format: int32
minimum: 1
type: integer
motd:
type: string
onlineMode:
default: true
type: boolean
pvp:
default: true
type: boolean
type: object
resources:
properties:
cpuLimit:
default: "2"
type: string
cpuRequest:
default: 500m
type: string
memoryLimitMB:
default: 2048
format: int32
minimum: 256
type: integer
memoryRequestMB:
format: int32
minimum: 256
type: integer
type: object
serviceType:
default: ClusterIP
enum:
- ClusterIP
- NodePort
- LoadBalancer
type: string
storageSize:
default: 1Gi
type: string
type:
enum:
- STATEFUL
- STATELESS
type: string
required:
- type
type: object
status:
properties:
conditions:
items:
description: Condition contains details for one aspect of the current
state of this API Resource.
properties:
lastTransitionTime:
description: |-
lastTransitionTime is the last time the condition transitioned from one status to another.
This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable.
format: date-time
type: string
message:
description: |-
message is a human readable message indicating details about the transition.
This may be an empty string.
maxLength: 32768
type: string
observedGeneration:
description: |-
observedGeneration represents the .metadata.generation that the condition was set based upon.
For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date
with respect to the current state of the instance.
format: int64
minimum: 0
type: integer
reason:
description: |-
reason contains a programmatic identifier indicating the reason for the condition's last transition.
Producers of specific condition types may define expected values and meanings for this field,
and whether the values are considered a guaranteed API.
The value should be a CamelCase string.
This field may not be empty.
maxLength: 1024
minLength: 1
pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$
type: string
status:
description: status of the condition, one of True, False, Unknown.
enum:
- "True"
- "False"
- Unknown
type: string
type:
description: type of condition in CamelCase or in foo.example.com/CamelCase.
maxLength: 316
pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$
type: string
required:
- lastTransitionTime
- message
- reason
- status
- type
type: object
type: array
x-kubernetes-list-map-keys:
- type
x-kubernetes-list-type: map
endpoint:
type: string
message:
type: string
observedGeneration:
format: int64
type: integer
phase:
type: string
readyReplicas:
format: int32
type: integer
replicas:
format: int32
type: integer
type: object
type: object
served: true
storage: true
subresources:
status: {}
@@ -0,0 +1,275 @@
---
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.21.0
name: reverseproxyservers.minikura.kirameki.cafe
spec:
group: minikura.kirameki.cafe
names:
kind: ReverseProxyServer
listKind: ReverseProxyServerList
plural: reverseproxyservers
shortNames:
- rps
singular: reverseproxyserver
scope: Namespaced
versions:
- additionalPrinterColumns:
- jsonPath: .spec.type
name: Type
type: string
- jsonPath: .spec.externalAddress
name: Address
type: string
- jsonPath: .status.phase
name: Phase
type: string
- jsonPath: .status.readyReplicas
name: Ready
type: string
- jsonPath: .metadata.creationTimestamp
name: Age
type: date
name: v1alpha1
schema:
openAPIV3Schema:
properties:
apiVersion:
description: |-
APIVersion defines the versioned schema of this representation of an object.
Servers should convert recognized schemas to the latest internal value, and
may reject unrecognized values.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
type: string
kind:
description: |-
Kind is a string value representing the REST resource this object represents.
Servers may infer this from the endpoint the client submits requests to.
Cannot be updated.
In CamelCase.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
type: string
metadata:
type: object
spec:
properties:
apiKeySecretRef:
type: string
backendSelector:
description: |-
A label selector is a label query over a set of resources. The result of matchLabels and
matchExpressions are ANDed. An empty label selector matches all objects. A null
label selector matches no objects.
properties:
matchExpressions:
description: matchExpressions is a list of label selector requirements.
The requirements are ANDed.
items:
description: |-
A label selector requirement is a selector that contains values, a key, and an operator that
relates the key and values.
properties:
key:
description: key is the label key that the selector applies
to.
type: string
operator:
description: |-
operator represents a key's relationship to a set of values.
Valid operators are In, NotIn, Exists and DoesNotExist.
type: string
values:
description: |-
values is an array of string values. If the operator is In or NotIn,
the values array must be non-empty. If the operator is Exists or DoesNotExist,
the values array must be empty. This array is replaced during a strategic
merge patch.
items:
type: string
type: array
x-kubernetes-list-type: atomic
required:
- key
- operator
type: object
type: array
x-kubernetes-list-type: atomic
matchLabels:
additionalProperties:
type: string
description: |-
matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels
map is equivalent to an element of matchExpressions, whose key field is "key", the
operator is "In", and the values array contains only "value". The requirements are ANDed.
type: object
type: object
x-kubernetes-map-type: atomic
description:
type: string
env:
items:
properties:
name:
type: string
value:
type: string
required:
- name
type: object
type: array
externalAddress:
type: string
externalPort:
format: int32
maximum: 65535
minimum: 1
type: integer
jvm:
properties:
heapPercent:
default: 80
format: int32
maximum: 100
minimum: 1
type: integer
opts:
type: string
useAikarFlags:
type: boolean
useMeowIceFlags:
type: boolean
type: object
listenPort:
default: 25565
format: int32
maximum: 65535
minimum: 1
type: integer
nodePort:
format: int32
maximum: 32767
minimum: 30000
type: integer
resources:
properties:
cpuLimit:
default: "2"
type: string
cpuRequest:
default: 500m
type: string
memoryLimitMB:
default: 2048
format: int32
minimum: 256
type: integer
memoryRequestMB:
format: int32
minimum: 256
type: integer
type: object
serviceType:
default: LoadBalancer
enum:
- ClusterIP
- NodePort
- LoadBalancer
type: string
type:
enum:
- VELOCITY
- BUNGEECORD
type: string
required:
- externalAddress
- externalPort
- type
type: object
status:
properties:
backends:
items:
type: string
type: array
conditions:
items:
description: Condition contains details for one aspect of the current
state of this API Resource.
properties:
lastTransitionTime:
description: |-
lastTransitionTime is the last time the condition transitioned from one status to another.
This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable.
format: date-time
type: string
message:
description: |-
message is a human readable message indicating details about the transition.
This may be an empty string.
maxLength: 32768
type: string
observedGeneration:
description: |-
observedGeneration represents the .metadata.generation that the condition was set based upon.
For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date
with respect to the current state of the instance.
format: int64
minimum: 0
type: integer
reason:
description: |-
reason contains a programmatic identifier indicating the reason for the condition's last transition.
Producers of specific condition types may define expected values and meanings for this field,
and whether the values are considered a guaranteed API.
The value should be a CamelCase string.
This field may not be empty.
maxLength: 1024
minLength: 1
pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$
type: string
status:
description: status of the condition, one of True, False, Unknown.
enum:
- "True"
- "False"
- Unknown
type: string
type:
description: type of condition in CamelCase or in foo.example.com/CamelCase.
maxLength: 316
pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$
type: string
required:
- lastTransitionTime
- message
- reason
- status
- type
type: object
type: array
x-kubernetes-list-map-keys:
- type
x-kubernetes-list-type: map
endpoint:
type: string
message:
type: string
observedGeneration:
format: int64
type: integer
phase:
type: string
readyReplicas:
format: int32
type: integer
replicas:
format: int32
type: integer
type: object
type: object
served: true
storage: true
subresources:
status: {}
+64
View File
@@ -0,0 +1,64 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: minikura-operator
labels:
app: minikura-operator
spec:
replicas: 1
selector:
matchLabels:
app: minikura-operator
template:
metadata:
labels:
app: minikura-operator
spec:
serviceAccountName: minikura-operator
securityContext:
runAsNonRoot: true
seccompProfile:
type: RuntimeDefault
containers:
- name: operator
image: minikura-operator:latest
args:
- --leader-elect
env:
- name: KUBERNETES_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
- name: NODE_ENV
value: production
ports:
- name: metrics
containerPort: 8080
- name: health
containerPort: 8081
livenessProbe:
httpGet:
path: /healthz
port: health
initialDelaySeconds: 15
periodSeconds: 20
readinessProbe:
httpGet:
path: /readyz
port: health
initialDelaySeconds: 5
periodSeconds: 10
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop:
- ALL
resources:
requests:
cpu: 50m
memory: 128Mi
limits:
cpu: 500m
memory: 256Mi
terminationGracePeriodSeconds: 10
+88
View File
@@ -0,0 +1,88 @@
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: minikura-operator-role
rules:
- apiGroups:
- ""
resources:
- configmaps
- services
verbs:
- create
- delete
- get
- list
- patch
- update
- watch
- apiGroups:
- ""
resources:
- events
verbs:
- create
- patch
- apiGroups:
- ""
resources:
- pods
verbs:
- get
- list
- watch
- apiGroups:
- apps
resources:
- deployments
- statefulsets
verbs:
- create
- delete
- get
- list
- patch
- update
- watch
- apiGroups:
- coordination.k8s.io
resources:
- leases
verbs:
- create
- delete
- get
- list
- patch
- update
- watch
- apiGroups:
- minikura.kirameki.cafe
resources:
- minecraftservers
- reverseproxyservers
verbs:
- create
- delete
- get
- list
- patch
- update
- watch
- apiGroups:
- minikura.kirameki.cafe
resources:
- minecraftservers/finalizers
- reverseproxyservers/finalizers
verbs:
- update
- apiGroups:
- minikura.kirameki.cafe
resources:
- minecraftservers/status
- reverseproxyservers/status
verbs:
- get
- patch
- update
+17
View File
@@ -0,0 +1,17 @@
apiVersion: v1
kind: ServiceAccount
metadata:
name: minikura-operator
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: minikura-operator-rolebinding
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: minikura-operator-role
subjects:
- kind: ServiceAccount
name: minikura-operator
namespace: minikura
@@ -0,0 +1,44 @@
apiVersion: minikura.kirameki.cafe/v1alpha1
kind: MinecraftServer
metadata:
name: survival
spec:
type: STATEFUL
description: Example SMP server
jarType: PAPER
minecraftVersion: "1.20.4"
listenPort: 25565
serviceType: ClusterIP
storageSize: 5Gi
resources:
memoryLimitMB: 4096
memoryRequestMB: 2048
cpuRequest: 500m
cpuLimit: "2"
jvm:
useAikarFlags: true
heapPercent: 80
properties:
difficulty: HARD
gameMode: SURVIVAL
maxPlayers: 20
pvp: true
onlineMode: true
motd: A Minikura server
---
apiVersion: minikura.kirameki.cafe/v1alpha1
kind: MinecraftServer
metadata:
name: lobby
labels:
role: lobby
spec:
type: STATELESS
jarType: PAPER
minecraftVersion: "1.20.4"
serviceType: ClusterIP
resources:
memoryLimitMB: 2048
properties:
gameMode: ADVENTURE
maxPlayers: 100
@@ -0,0 +1,17 @@
apiVersion: minikura.kirameki.cafe/v1alpha1
kind: ReverseProxyServer
metadata:
name: edge
spec:
type: VELOCITY
description: Example Velocity proxy
externalAddress: play.example.com
externalPort: 25565
listenPort: 25577
serviceType: LoadBalancer
resources:
memoryLimitMB: 1024
cpuRequest: 250m
cpuLimit: "1"
jvm:
heapPercent: 80