mirror of
https://github.com/YuzuZensai/Minikura.git
synced 2026-09-13 18:59:25 +00:00
✨ feat: add Go Kubernetes operator
This commit is contained in:
@@ -0,0 +1,172 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sort"
|
||||
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
ctrl "sigs.k8s.io/controller-runtime"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/handler"
|
||||
"sigs.k8s.io/controller-runtime/pkg/reconcile"
|
||||
|
||||
v1alpha1 "github.com/YuzuZensai/Minikura/operator/api/v1alpha1"
|
||||
"github.com/YuzuZensai/Minikura/operator/internal/resources"
|
||||
)
|
||||
|
||||
type ReverseProxyServerReconciler struct {
|
||||
client.Client
|
||||
Scheme *runtime.Scheme
|
||||
}
|
||||
|
||||
// +kubebuilder:rbac:groups=minikura.kirameki.cafe,resources=reverseproxyservers,verbs=get;list;watch;create;update;patch;delete
|
||||
// +kubebuilder:rbac:groups=minikura.kirameki.cafe,resources=reverseproxyservers/status,verbs=get;update;patch
|
||||
// +kubebuilder:rbac:groups=minikura.kirameki.cafe,resources=reverseproxyservers/finalizers,verbs=update
|
||||
|
||||
func (r *ReverseProxyServerReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
|
||||
var rp v1alpha1.ReverseProxyServer
|
||||
if err := r.Get(ctx, req.NamespacedName, &rp); err != nil {
|
||||
return ctrl.Result{}, client.IgnoreNotFound(err)
|
||||
}
|
||||
|
||||
if !rp.DeletionTimestamp.IsZero() {
|
||||
return ctrl.Result{}, nil
|
||||
}
|
||||
|
||||
if err := apply(ctx, r.Client, &rp, resources.ProxyConfigMap(&rp), r.Scheme); err != nil {
|
||||
return r.fail(ctx, &rp, "ConfigMapFailed", err)
|
||||
}
|
||||
|
||||
if err := apply(ctx, r.Client, &rp, resources.ProxyService(&rp), r.Scheme); err != nil {
|
||||
return r.fail(ctx, &rp, "ServiceFailed", err)
|
||||
}
|
||||
|
||||
if err := apply(ctx, r.Client, &rp, resources.ProxyDeployment(&rp), r.Scheme); err != nil {
|
||||
return r.fail(ctx, &rp, "DeploymentFailed", err)
|
||||
}
|
||||
|
||||
return ctrl.Result{}, r.updateStatus(ctx, &rp)
|
||||
}
|
||||
|
||||
func (r *ReverseProxyServerReconciler) backends(ctx context.Context, rp *v1alpha1.ReverseProxyServer) ([]string, error) {
|
||||
selector := labels.Everything()
|
||||
if rp.Spec.BackendSelector != nil {
|
||||
s, err := metav1.LabelSelectorAsSelector(rp.Spec.BackendSelector)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid backendSelector: %w", err)
|
||||
}
|
||||
selector = s
|
||||
}
|
||||
|
||||
var list v1alpha1.MinecraftServerList
|
||||
if err := r.List(ctx, &list,
|
||||
client.InNamespace(rp.Namespace),
|
||||
client.MatchingLabelsSelector{Selector: selector},
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
names := make([]string, 0, len(list.Items))
|
||||
for _, mc := range list.Items {
|
||||
names = append(names, mc.Name)
|
||||
}
|
||||
sort.Strings(names)
|
||||
return names, nil
|
||||
}
|
||||
|
||||
func (r *ReverseProxyServerReconciler) updateStatus(ctx context.Context, rp *v1alpha1.ReverseProxyServer) error {
|
||||
name := resources.ProxyName(rp.Spec.Type, rp.Name)
|
||||
key := client.ObjectKey{Name: name, Namespace: rp.Namespace}
|
||||
|
||||
var ready, replicas int32
|
||||
var dep appsv1.Deployment
|
||||
if err := r.Get(ctx, key, &dep); err == nil {
|
||||
ready, replicas = dep.Status.ReadyReplicas, dep.Status.Replicas
|
||||
} else if !apierrors.IsNotFound(err) {
|
||||
return err
|
||||
}
|
||||
|
||||
backends, err := r.backends(ctx, rp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
endpoint := ""
|
||||
var svc corev1.Service
|
||||
if err := r.Get(ctx, key, &svc); err == nil {
|
||||
endpoint = serviceEndpoint(&svc, rp.Namespace)
|
||||
} else if !apierrors.IsNotFound(err) {
|
||||
return err
|
||||
}
|
||||
|
||||
phase := v1alpha1.PhasePending
|
||||
if ready > 0 {
|
||||
phase = v1alpha1.PhaseRunning
|
||||
}
|
||||
|
||||
patch := client.MergeFrom(rp.DeepCopy())
|
||||
rp.Status.Phase = phase
|
||||
rp.Status.ReadyReplicas = ready
|
||||
rp.Status.Replicas = replicas
|
||||
rp.Status.Endpoint = endpoint
|
||||
rp.Status.Backends = backends
|
||||
rp.Status.ObservedGeneration = rp.Generation
|
||||
rp.Status.Message = ""
|
||||
setCondition(&rp.Status.Conditions, metav1.Condition{
|
||||
Type: v1alpha1.ConditionReady,
|
||||
Status: conditionStatus(ready > 0),
|
||||
Reason: phase,
|
||||
ObservedGeneration: rp.Generation,
|
||||
})
|
||||
|
||||
return r.Status().Patch(ctx, rp, patch)
|
||||
}
|
||||
|
||||
func (r *ReverseProxyServerReconciler) fail(ctx context.Context, rp *v1alpha1.ReverseProxyServer, reason string, cause error) (ctrl.Result, error) {
|
||||
patch := client.MergeFrom(rp.DeepCopy())
|
||||
rp.Status.Phase = v1alpha1.PhaseFailed
|
||||
rp.Status.Message = cause.Error()
|
||||
setCondition(&rp.Status.Conditions, metav1.Condition{
|
||||
Type: v1alpha1.ConditionReady,
|
||||
Status: metav1.ConditionFalse,
|
||||
Reason: reason,
|
||||
Message: cause.Error(),
|
||||
ObservedGeneration: rp.Generation,
|
||||
})
|
||||
|
||||
if err := r.Status().Patch(ctx, rp, patch); err != nil {
|
||||
return ctrl.Result{}, fmt.Errorf("%w (status patch failed: %v)", cause, err)
|
||||
}
|
||||
return ctrl.Result{}, cause
|
||||
}
|
||||
|
||||
func (r *ReverseProxyServerReconciler) proxiesForServer(ctx context.Context, obj client.Object) []reconcile.Request {
|
||||
var list v1alpha1.ReverseProxyServerList
|
||||
if err := r.List(ctx, &list, client.InNamespace(obj.GetNamespace())); err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
reqs := make([]reconcile.Request, 0, len(list.Items))
|
||||
for _, rp := range list.Items {
|
||||
reqs = append(reqs, reconcile.Request{
|
||||
NamespacedName: client.ObjectKey{Name: rp.Name, Namespace: rp.Namespace},
|
||||
})
|
||||
}
|
||||
return reqs
|
||||
}
|
||||
|
||||
func (r *ReverseProxyServerReconciler) SetupWithManager(mgr ctrl.Manager) error {
|
||||
return ctrl.NewControllerManagedBy(mgr).
|
||||
For(&v1alpha1.ReverseProxyServer{}).
|
||||
Owns(&appsv1.Deployment{}).
|
||||
Owns(&corev1.Service{}).
|
||||
Owns(&corev1.ConfigMap{}).
|
||||
Watches(&v1alpha1.MinecraftServer{}, handler.EnqueueRequestsFromMapFunc(r.proxiesForServer)).
|
||||
Complete(r)
|
||||
}
|
||||
Reference in New Issue
Block a user