Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions internal/controller/chunksgenerator_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import (
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/builder"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/event"
"sigs.k8s.io/controller-runtime/pkg/handler"
"sigs.k8s.io/controller-runtime/pkg/log"
"sigs.k8s.io/controller-runtime/pkg/predicate"
Expand Down Expand Up @@ -368,6 +369,9 @@ func (r *ChunksGeneratorReconciler) findDependents(ctx context.Context, obj clie
// SetupWithManager sets up the controller with the Manager.
func (r *ChunksGeneratorReconciler) SetupWithManager(mgr ctrl.Manager) error {
return ctrl.NewControllerManagedBy(mgr).
WithEventFilter(predicate.Funcs{
DeleteFunc: func(_ event.DeleteEvent) bool { return false },
}).
For(&operatorv1alpha1.ChunksGenerator{}, builder.WithPredicates(predicate.GenerationChangedPredicate{})).
Watches(&operatorv1alpha1.SourceCrawler{}, handler.EnqueueRequestsFromMapFunc(r.findDependents), builder.WithPredicates(controllerutils.FilesProcessedChangedPredicate{})).
Watches(&operatorv1alpha1.DocumentProcessor{}, handler.EnqueueRequestsFromMapFunc(r.findDependents), builder.WithPredicates(controllerutils.FilesProcessedChangedPredicate{})).
Expand Down
3 changes: 1 addition & 2 deletions internal/controller/destinationsyncer_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,7 @@ func (r *DestinationSyncerReconciler) Reconcile(ctx context.Context, req ctrl.Re

destinationSyncCR := &operatorv1alpha1.DestinationSyncer{}
if err := r.Get(ctx, req.NamespacedName, destinationSyncCR); err != nil {
logger.Error(err, "failed to get DestinationSyncer CR")
return ctrl.Result{}, err
return ctrl.Result{}, client.IgnoreNotFound(err)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you add a comment for why this is done here and other places also

}

if err := controllerutils.StatusPatch(ctx, r.Client, destinationSyncCR, func() {
Expand Down
4 changes: 4 additions & 0 deletions internal/controller/documentprocessor_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import (
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/builder"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/event"
"sigs.k8s.io/controller-runtime/pkg/handler"
"sigs.k8s.io/controller-runtime/pkg/log"
"sigs.k8s.io/controller-runtime/pkg/predicate"
Expand Down Expand Up @@ -522,6 +523,9 @@ func (r *DocumentProcessorReconciler) findDependents(ctx context.Context, obj cl
// Watches on other stage types trigger reconcile when an upstream dependency's status changes.
func (r *DocumentProcessorReconciler) SetupWithManager(mgr ctrl.Manager) error {
return ctrl.NewControllerManagedBy(mgr).
WithEventFilter(predicate.Funcs{
DeleteFunc: func(_ event.DeleteEvent) bool { return false },
}).
For(&operatorv1alpha1.DocumentProcessor{}, builder.WithPredicates(predicate.GenerationChangedPredicate{})).
Watches(&operatorv1alpha1.SourceCrawler{}, handler.EnqueueRequestsFromMapFunc(r.findDependents), builder.WithPredicates(controllerutils.FilesProcessedChangedPredicate{})).
Watches(&operatorv1alpha1.ChunksGenerator{}, handler.EnqueueRequestsFromMapFunc(r.findDependents), builder.WithPredicates(controllerutils.FilesProcessedChangedPredicate{})).
Expand Down
3 changes: 1 addition & 2 deletions internal/controller/sourcecrawler_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,7 @@ func (r *SourceCrawlerReconciler) Reconcile(ctx context.Context, req ctrl.Reques

sourceCrawlerCR := &operatorv1alpha1.SourceCrawler{}
if err := r.Get(ctx, req.NamespacedName, sourceCrawlerCR); err != nil {
logger.Error(err, "failed to get SourceCrawler CR")
return ctrl.Result{}, err
return ctrl.Result{}, client.IgnoreNotFound(err)
}

if err := controllerutils.StatusPatch(ctx, r.Client, sourceCrawlerCR, func() {
Expand Down
63 changes: 56 additions & 7 deletions internal/controller/unstructureddatapipeline_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import (
"sigs.k8s.io/controller-runtime/pkg/builder"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
"sigs.k8s.io/controller-runtime/pkg/event"
"sigs.k8s.io/controller-runtime/pkg/log"
"sigs.k8s.io/controller-runtime/pkg/predicate"

Expand All @@ -40,6 +41,7 @@ import (
const (
UnstructuredDataPipelineControllerName = "UnstructuredDataPipeline"
PipelineLabel = "operator.dataverse.redhat.com/unstructured-data-pipeline"
UnstructuredDataPipelineFinalizer = "operator.dataverse.redhat.com/unstructured-data-pipeline-finalizer"
)

var (
Expand Down Expand Up @@ -69,6 +71,20 @@ type UnstructuredDataPipelineReconciler struct {

func (r *UnstructuredDataPipelineReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
logger := log.FromContext(ctx)

// Fetch the CR before the health check so that deletion is never blocked
// by an unhealthy ControllerConfig.
unstructuredDataPipelineCR := &operatorv1alpha1.UnstructuredDataPipeline{}
if err := r.Get(ctx, req.NamespacedName, unstructuredDataPipelineCR); err != nil {
return ctrl.Result{}, client.IgnoreNotFound(err)
}
// DeepCopy to avoid mutating the shared informer cache
unstructuredDataPipelineCR = unstructuredDataPipelineCR.DeepCopy()

if !unstructuredDataPipelineCR.DeletionTimestamp.IsZero() {
return r.handleDeletion(ctx, unstructuredDataPipelineCR)
}

logger.Info("reconciling", "controller", UnstructuredDataPipelineControllerName)

isHealthy, err := IsConfigCRHealthy(ctx, r.Client, req.Namespace)
Expand All @@ -81,13 +97,19 @@ func (r *UnstructuredDataPipelineReconciler) Reconcile(ctx context.Context, req
return ctrl.Result{RequeueAfter: 10 * time.Second}, nil
}

unstructuredDataPipelineCR := &operatorv1alpha1.UnstructuredDataPipeline{}
if err := r.Get(ctx, req.NamespacedName, unstructuredDataPipelineCR); err != nil {
logger.Error(err, "failed to get UnstructuredDataPipeline CR")
return ctrl.Result{}, err
// Ensure the finalizer is registered before any other work.
if !controllerutil.ContainsFinalizer(unstructuredDataPipelineCR, UnstructuredDataPipelineFinalizer) {
patch := client.MergeFrom(unstructuredDataPipelineCR.DeepCopy())
controllerutil.AddFinalizer(unstructuredDataPipelineCR, UnstructuredDataPipelineFinalizer)
if err := r.Patch(ctx, unstructuredDataPipelineCR, patch); err != nil {
logger.Error(err, "failed to add finalizer")
return ctrl.Result{}, err
}
// Adding a finalizer only bumps metadata, not spec, so generation stays the
// same and GenerationChangedPredicate would swallow the resulting UPDATE
// event. Explicit requeue ensures we continue without relying on the watch.
return ctrl.Result{Requeue: true}, nil
Comment thread
PuneetPunamiya marked this conversation as resolved.
}
// DeepCopy to avoid mutating the shared informer cache
unstructuredDataPipelineCR = unstructuredDataPipelineCR.DeepCopy()

stages := unstructuredDataPipelineCR.Spec.Stages
if err := operatorv1alpha1.ValidateStages(stages); err != nil {
Expand Down Expand Up @@ -353,10 +375,37 @@ func (r *UnstructuredDataPipelineReconciler) ensureChildCR(ctx context.Context,
return r.markStageCreated(ctx, unstructuredDataPipelineCR, stage.Name)
}

func (r *UnstructuredDataPipelineReconciler) handleDeletion(ctx context.Context, pipeline *operatorv1alpha1.UnstructuredDataPipeline) (ctrl.Result, error) {
logger := log.FromContext(ctx)

if !controllerutil.ContainsFinalizer(pipeline, UnstructuredDataPipelineFinalizer) {
return ctrl.Result{}, nil
}

patch := client.MergeFrom(pipeline.DeepCopy())
controllerutil.RemoveFinalizer(pipeline, UnstructuredDataPipelineFinalizer)
if err := r.Patch(ctx, pipeline, patch); err != nil {
Comment thread
PuneetPunamiya marked this conversation as resolved.
if client.IgnoreNotFound(err) == nil {
return ctrl.Result{}, nil
}
logger.Error(err, "failed to remove finalizer")
return ctrl.Result{}, err
}
logger.Info("finalizer removed, pipeline deletion complete", "pipeline", pipeline.Name)
return ctrl.Result{}, nil
}

// SetupWithManager sets up the controller with the Manager.
func (r *UnstructuredDataPipelineReconciler) SetupWithManager(mgr ctrl.Manager) error {
return ctrl.NewControllerManagedBy(mgr).
For(&operatorv1alpha1.UnstructuredDataPipeline{}, builder.WithPredicates(predicate.GenerationChangedPredicate{})).
For(&operatorv1alpha1.UnstructuredDataPipeline{}, builder.WithPredicates(predicate.Or(
predicate.GenerationChangedPredicate{},
predicate.Funcs{
UpdateFunc: func(e event.UpdateEvent) bool {
return !e.ObjectNew.GetDeletionTimestamp().IsZero()
},
},
))).
Owns(&operatorv1alpha1.SourceCrawler{}, builder.WithPredicates(predicate.GenerationChangedPredicate{})).
Owns(&operatorv1alpha1.DocumentProcessor{}, builder.WithPredicates(predicate.GenerationChangedPredicate{})).
Owns(&operatorv1alpha1.ChunksGenerator{}, builder.WithPredicates(predicate.GenerationChangedPredicate{})).
Expand Down
4 changes: 4 additions & 0 deletions internal/controller/vectorembeddingsgenerator_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import (
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/builder"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/event"
"sigs.k8s.io/controller-runtime/pkg/handler"
"sigs.k8s.io/controller-runtime/pkg/log"
"sigs.k8s.io/controller-runtime/pkg/predicate"
Expand Down Expand Up @@ -397,6 +398,9 @@ func (r *VectorEmbeddingsGeneratorReconciler) findDependents(ctx context.Context
// SetupWithManager sets up the controller with the Manager.
func (r *VectorEmbeddingsGeneratorReconciler) SetupWithManager(mgr ctrl.Manager) error {
return ctrl.NewControllerManagedBy(mgr).
WithEventFilter(predicate.Funcs{
DeleteFunc: func(_ event.DeleteEvent) bool { return false },
}).
For(&operatorv1alpha1.VectorEmbeddingsGenerator{}, builder.WithPredicates(predicate.GenerationChangedPredicate{})).
Watches(&operatorv1alpha1.SourceCrawler{}, handler.EnqueueRequestsFromMapFunc(r.findDependents), builder.WithPredicates(controllerutils.FilesProcessedChangedPredicate{})).
Watches(&operatorv1alpha1.DocumentProcessor{}, handler.EnqueueRequestsFromMapFunc(r.findDependents), builder.WithPredicates(controllerutils.FilesProcessedChangedPredicate{})).
Expand Down
13 changes: 13 additions & 0 deletions test/e2e/unstructured_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -658,6 +658,19 @@ func TestUnstructuredDataLoad(t *testing.T) {
t.Fatal(err)
}

// Wait for the finalizer to complete and the object to be fully removed.
if err := apimachinerywait.PollUntilContextTimeout(ctx, 5*time.Second, 2*time.Minute, false,
func(ctx context.Context) (bool, error) {
err := kubeClient.Resources(testNamespace).Get(ctx, dataPipelineCRName, testNamespace, &v1alpha1.UnstructuredDataPipeline{})
if apierrors.IsNotFound(err) {
return true, nil
}
return false, err
},
); err != nil {
t.Fatalf("timed out waiting for pipeline %s to be deleted: %v", dataPipelineCRName, err)
}

return ctx
},
)
Expand Down
Loading