mirror of
https://github.com/YuzuZensai/Minikura.git
synced 2026-09-13 10:49:21 +00:00
♻️ refactor: migrate to Go Kubernetes operator
This commit is contained in:
+9
-3
@@ -1,5 +1,6 @@
|
||||
IMG ?= minikura-operator:latest
|
||||
NAMESPACE ?= minikura
|
||||
CONTROLLER_GEN_VERSION ?= v0.20.0
|
||||
CONTROLLER_GEN ?= $(shell go env GOPATH)/bin/controller-gen
|
||||
|
||||
.PHONY: all
|
||||
@@ -7,7 +8,7 @@ all: generate manifests fmt vet test build
|
||||
|
||||
.PHONY: controller-gen
|
||||
controller-gen:
|
||||
@test -x $(CONTROLLER_GEN) || go install sigs.k8s.io/controller-tools/cmd/controller-gen@latest
|
||||
@test -x $(CONTROLLER_GEN) || go install sigs.k8s.io/controller-tools/cmd/controller-gen@$(CONTROLLER_GEN_VERSION)
|
||||
|
||||
.PHONY: generate
|
||||
generate: controller-gen
|
||||
@@ -52,6 +53,11 @@ uninstall-crds:
|
||||
|
||||
.PHONY: deploy
|
||||
deploy: manifests
|
||||
kubectl create namespace $(NAMESPACE) --dry-run=client -o yaml | kubectl apply -f -
|
||||
kubectl apply -f config/crd
|
||||
kubectl apply -n $(NAMESPACE) -f config/rbac
|
||||
kubectl apply -n $(NAMESPACE) -f config/manager
|
||||
kubectl apply -f config/rbac/role.yaml
|
||||
kubectl apply -n $(NAMESPACE) -f config/rbac/service_account.yaml
|
||||
kubectl apply -n $(NAMESPACE) -f config/rbac/backend.yaml
|
||||
kubectl patch clusterrolebinding minikura-operator-rolebinding --type=json -p='[{"op":"replace","path":"/subjects/0/namespace","value":"$(NAMESPACE)"}]'
|
||||
kubectl patch clusterrolebinding minikura-backend-operator-resources --type=json -p='[{"op":"replace","path":"/subjects/0/namespace","value":"$(NAMESPACE)"}]'
|
||||
sed 's|minikura-operator:latest|$(IMG)|' config/manager/deployment.yaml | kubectl apply -n $(NAMESPACE) -f -
|
||||
|
||||
@@ -141,7 +141,7 @@ type MinecraftServerStatus struct {
|
||||
// +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="Ready",type=integer,JSONPath=`.status.readyReplicas`
|
||||
// +kubebuilder:printcolumn:name="Endpoint",type=string,JSONPath=`.status.endpoint`
|
||||
// +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp`
|
||||
type MinecraftServer struct {
|
||||
|
||||
@@ -89,7 +89,7 @@ type ReverseProxyServerStatus struct {
|
||||
// +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="Ready",type=integer,JSONPath=`.status.readyReplicas`
|
||||
// +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp`
|
||||
type ReverseProxyServer struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
|
||||
@@ -28,7 +28,7 @@ spec:
|
||||
type: string
|
||||
- jsonPath: .status.readyReplicas
|
||||
name: Ready
|
||||
type: string
|
||||
type: integer
|
||||
- jsonPath: .status.endpoint
|
||||
name: Endpoint
|
||||
type: string
|
||||
|
||||
@@ -28,7 +28,7 @@ spec:
|
||||
type: string
|
||||
- jsonPath: .status.readyReplicas
|
||||
name: Ready
|
||||
type: string
|
||||
type: integer
|
||||
- jsonPath: .metadata.creationTimestamp
|
||||
name: Age
|
||||
type: date
|
||||
|
||||
@@ -29,8 +29,6 @@ spec:
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: metadata.namespace
|
||||
- name: NODE_ENV
|
||||
value: production
|
||||
ports:
|
||||
- name: metrics
|
||||
containerPort: 8080
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: minikura-backend
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
name: minikura-backend-operator-resources
|
||||
rules:
|
||||
- apiGroups: [""]
|
||||
resources: ["secrets"]
|
||||
verbs: ["get", "list", "create", "update", "delete"]
|
||||
- apiGroups: ["minikura.kirameki.cafe"]
|
||||
resources: ["minecraftservers", "reverseproxyservers"]
|
||||
verbs: ["get", "list", "create", "update", "delete"]
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
name: minikura-backend-operator-resources
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: minikura-backend-operator-resources
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: minikura-backend
|
||||
namespace: minikura
|
||||
@@ -50,10 +50,38 @@ func (r *ReverseProxyServerReconciler) Reconcile(ctx context.Context, req ctrl.R
|
||||
if err := apply(ctx, r.Client, &rp, resources.ProxyDeployment(&rp), r.Scheme); err != nil {
|
||||
return r.fail(ctx, &rp, "DeploymentFailed", err)
|
||||
}
|
||||
if err := r.pruneStaleResources(ctx, &rp); err != nil {
|
||||
return r.fail(ctx, &rp, "PruneFailed", err)
|
||||
}
|
||||
|
||||
return ctrl.Result{}, r.updateStatus(ctx, &rp)
|
||||
}
|
||||
|
||||
func (r *ReverseProxyServerReconciler) pruneStaleResources(ctx context.Context, rp *v1alpha1.ReverseProxyServer) error {
|
||||
current := resources.ProxyName(rp.Spec.Type, rp.Name)
|
||||
for _, kind := range []v1alpha1.ProxyKind{v1alpha1.ProxyVelocity, v1alpha1.ProxyBungeeCord} {
|
||||
name := resources.ProxyName(kind, rp.Name)
|
||||
if name == current {
|
||||
continue
|
||||
}
|
||||
for _, obj := range []client.Object{&appsv1.Deployment{}, &corev1.Service{}, &corev1.ConfigMap{}} {
|
||||
key := client.ObjectKey{Name: name, Namespace: rp.Namespace}
|
||||
if err := r.Get(ctx, key, obj); err != nil {
|
||||
if apierrors.IsNotFound(err) {
|
||||
continue
|
||||
}
|
||||
return err
|
||||
}
|
||||
if metav1.IsControlledBy(obj, rp) {
|
||||
if err := r.Delete(ctx, obj); err != nil && !apierrors.IsNotFound(err) {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *ReverseProxyServerReconciler) backends(ctx context.Context, rp *v1alpha1.ReverseProxyServer) ([]string, error) {
|
||||
selector := labels.Everything()
|
||||
if rp.Spec.BackendSelector != nil {
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
|
||||
const (
|
||||
MinecraftImage = "itzg/minecraft-server"
|
||||
ProxyImage = "itzg/mc-proxy:latest"
|
||||
ContainerPort = 25565
|
||||
DefaultHeapPercent = 80
|
||||
)
|
||||
|
||||
@@ -46,8 +46,8 @@ func ProxyService(rp *v1alpha1.ReverseProxyServer) *corev1.Service {
|
||||
|
||||
func proxyEnv(rp *v1alpha1.ReverseProxyServer) []corev1.EnvVar {
|
||||
env := []corev1.EnvVar{
|
||||
{Name: "EULA", Value: "TRUE"},
|
||||
{Name: "TYPE", Value: string(rp.Spec.Type)},
|
||||
{Name: "NETWORKADDRESS_CACHE_TTL", Value: "30"},
|
||||
{Name: "MINIKURA_EXTERNAL_ADDRESS", Value: rp.Spec.ExternalAddress},
|
||||
}
|
||||
|
||||
@@ -83,7 +83,7 @@ func ProxyDeployment(rp *v1alpha1.ReverseProxyServer) *appsv1.Deployment {
|
||||
Spec: corev1.PodSpec{
|
||||
Containers: []corev1.Container{{
|
||||
Name: "proxy",
|
||||
Image: MinecraftImage,
|
||||
Image: ProxyImage,
|
||||
Ports: []corev1.ContainerPort{{
|
||||
Name: "minecraft",
|
||||
ContainerPort: rp.Spec.ListenPort,
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
package resources
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
v1alpha1 "github.com/YuzuZensai/Minikura/operator/api/v1alpha1"
|
||||
)
|
||||
|
||||
func testProxy() *v1alpha1.ReverseProxyServer {
|
||||
return &v1alpha1.ReverseProxyServer{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "edge", Namespace: "minikura"},
|
||||
Spec: v1alpha1.ReverseProxyServerSpec{
|
||||
Type: v1alpha1.ProxyVelocity,
|
||||
ExternalAddress: "play.example.com",
|
||||
ExternalPort: 25565,
|
||||
ListenPort: 25577,
|
||||
Resources: v1alpha1.Resources{MemoryLimitMB: 512},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func TestProxyDeploymentUsesProxyImage(t *testing.T) {
|
||||
container := ProxyDeployment(testProxy()).Spec.Template.Spec.Containers[0]
|
||||
if container.Image != ProxyImage {
|
||||
t.Errorf("image = %q, want %q", container.Image, ProxyImage)
|
||||
}
|
||||
}
|
||||
|
||||
func TestProxyEnvCarriesRuntimeConfig(t *testing.T) {
|
||||
env := proxyEnv(testProxy())
|
||||
for _, want := range []struct{ key, value string }{
|
||||
{"TYPE", "VELOCITY"},
|
||||
{"NETWORKADDRESS_CACHE_TTL", "30"},
|
||||
{"MINIKURA_EXTERNAL_ADDRESS", "play.example.com"},
|
||||
} {
|
||||
got, ok := envValue(env, want.key)
|
||||
if !ok || got != want.value {
|
||||
t.Errorf("%s = %q, %v; want %q", want.key, got, ok, want.value)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user