mirror of
https://github.com/YuzuZensai/Minikura.git
synced 2026-09-13 10:49:21 +00:00
201 lines
6.2 KiB
Go
201 lines
6.2 KiB
Go
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)
|
|
}
|
|
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 {
|
|
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)
|
|
}
|