Browse Source

feat(model): implement ExternalModel controller

Thomas Zhang 2 tháng trước cách đây
mục cha
commit
4ed8b4cd47
1 tập tin đã thay đổi với 71 bổ sung11 xóa
  1. 71 11
      internal/controller/externalmodel_controller.go

+ 71 - 11
internal/controller/externalmodel_controller.go

@@ -19,12 +19,16 @@ package controller
 import (
 	"context"
 
+	apierrors "k8s.io/apimachinery/pkg/api/errors"
+	apimeta "k8s.io/apimachinery/pkg/api/meta"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/runtime"
 	ctrl "sigs.k8s.io/controller-runtime"
 	"sigs.k8s.io/controller-runtime/pkg/client"
 	logf "sigs.k8s.io/controller-runtime/pkg/log"
 
 	"github.com/LocoStack/loco-operator/api/v1alpha1"
+	"github.com/LocoStack/loco-operator/internal/reconciler"
 )
 
 // ExternalModelReconciler reconciles a ExternalModel object
@@ -36,28 +40,84 @@ type ExternalModelReconciler struct {
 // +kubebuilder:rbac:groups=locostack.com,resources=externalmodels,verbs=get;list;watch;create;update;patch;delete
 // +kubebuilder:rbac:groups=locostack.com,resources=externalmodels/status,verbs=get;update;patch
 // +kubebuilder:rbac:groups=locostack.com,resources=externalmodels/finalizers,verbs=update
+// +kubebuilder:rbac:groups=locostack.com,resources=components,verbs=get;list;watch
+// +kubebuilder:rbac:groups="",resources=secrets,verbs=get;list;watch
 
-// Reconcile is part of the main kubernetes reconciliation loop which aims to
-// move the current state of the cluster closer to the desired state.
-// TODO(user): Modify the Reconcile function to compare the state specified by
-// the ExternalModel object against the actual cluster state, and then
-// perform operations to make the cluster state reflect the state specified by
-// the user.
-//
-// For more details, check Reconcile and its Result here:
-// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.23.3/pkg/reconcile
 func (r *ExternalModelReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
-	_ = logf.FromContext(ctx)
+	log := logf.FromContext(ctx)
 
-	// TODO(user): your logic here
+	em := &v1alpha1.ExternalModel{}
+	if err := r.Get(ctx, req.NamespacedName, em); err != nil {
+		if apierrors.IsNotFound(err) {
+			return ctrl.Result{}, nil
+		}
+		log.Error(err, "Failed to get ExternalModel", "namespace", req.NamespacedName.Namespace, "name", req.NamespacedName.Name)
+		return ctrl.Result{}, err
+	}
 
+	patch := client.MergeFrom(em.DeepCopy())
+
+	gatewayBound, err := r.reconcileGatewayStatus(ctx, em)
+	if err != nil {
+		log.Error(err, "Failed to reconcile gateway status for ExternalModel", "namespace", req.NamespacedName.Namespace, "name", req.NamespacedName.Name)
+		return ctrl.Result{}, err
+	}
+	credentialsResolved, err := r.reconcileCredentials(ctx, em)
+	if err != nil {
+		log.Error(err, "Failed to reconcile credentials for ExternalModel", "namespace", req.NamespacedName.Namespace, "name", req.NamespacedName.Name)
+		return ctrl.Result{}, err
+	}
+
+	if gatewayBound && credentialsResolved {
+		apimeta.SetStatusCondition(&em.Status.Conditions, metav1.Condition{
+			Type:               "Ready",
+			Status:             metav1.ConditionTrue,
+			Reason:             "AllPreconditionsMet",
+			Message:            "Gateway exists and credentials resolve",
+			ObservedGeneration: em.Generation,
+		})
+	} else {
+		apimeta.SetStatusCondition(&em.Status.Conditions, metav1.Condition{
+			Type:               "Ready",
+			Status:             metav1.ConditionFalse,
+			Reason:             "PreconditionsNotMet",
+			Message:            "Gateway not found or credentials not resolved",
+			ObservedGeneration: em.Generation,
+		})
+	}
+
+	em.Status.ObservedGeneration = em.Generation
+	if err := r.Status().Patch(ctx, em, patch); err != nil {
+		return ctrl.Result{}, client.IgnoreNotFound(err)
+	}
+
+	log.Info("Reconciled ExternalModel", "namespace", em.Namespace, "name", em.Name)
 	return ctrl.Result{}, nil
 }
 
+func (r *ExternalModelReconciler) reconcileGatewayStatus(ctx context.Context, em *v1alpha1.ExternalModel) (bool, error) {
+	condition, err := reconciler.ReconcileGatewayStatus(ctx, r.Client, r.Scheme, em.Spec.StackRef.Name, em)
+	if err != nil {
+		return false, err
+	}
+	apimeta.SetStatusCondition(&em.Status.Conditions, *condition)
+	return condition.Status == metav1.ConditionTrue, nil
+}
+
+func (r *ExternalModelReconciler) reconcileCredentials(ctx context.Context, em *v1alpha1.ExternalModel) (bool, error) {
+	condition, err := reconciler.ReconcileCredentials(ctx, r.Client, r.Scheme, em.Spec.Auth, em)
+	if err != nil {
+		return false, err
+	}
+	apimeta.SetStatusCondition(&em.Status.Conditions, *condition)
+	return condition.Status == metav1.ConditionTrue, nil
+}
+
 // SetupWithManager sets up the controller with the Manager.
 func (r *ExternalModelReconciler) SetupWithManager(mgr ctrl.Manager) error {
 	return ctrl.NewControllerManagedBy(mgr).
 		For(&v1alpha1.ExternalModel{}).
+		Watches(&v1alpha1.Component{}, ComponentEventHandler("ExternalModel")).
 		Named("externalmodel").
 		Complete(r)
 }