github.com/tilt-dev/tilt@v0.33.15-0.20240515162809-0a22ed45d8a0/internal/controllers/core/session/reconciler.go (about)

     1  package session
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  
     7  	"sigs.k8s.io/controller-runtime/pkg/builder"
     8  
     9  	apierrors "k8s.io/apimachinery/pkg/api/errors"
    10  	"k8s.io/apimachinery/pkg/types"
    11  	ctrl "sigs.k8s.io/controller-runtime"
    12  	ctrlclient "sigs.k8s.io/controller-runtime/pkg/client"
    13  	"sigs.k8s.io/controller-runtime/pkg/reconcile"
    14  
    15  	"github.com/jonboulle/clockwork"
    16  
    17  	"github.com/tilt-dev/tilt/internal/controllers/apicmp"
    18  	"github.com/tilt-dev/tilt/internal/controllers/indexer"
    19  	"github.com/tilt-dev/tilt/internal/store"
    20  	"github.com/tilt-dev/tilt/internal/store/sessions"
    21  	"github.com/tilt-dev/tilt/pkg/apis/core/v1alpha1"
    22  )
    23  
    24  // Session reports on current CI/Up state, and determines
    25  // when Tilt should exit.
    26  //
    27  // Reads the Session Spec and updates the Session Status.
    28  //
    29  // Dispatches an event to the store for handling exits.
    30  type Reconciler struct {
    31  	client   ctrlclient.Client
    32  	st       store.RStore
    33  	requeuer *indexer.Requeuer
    34  	clock    clockwork.Clock
    35  }
    36  
    37  var _ reconcile.Reconciler = &Reconciler{}
    38  
    39  func NewReconciler(client ctrlclient.Client, st store.RStore, clock clockwork.Clock) *Reconciler {
    40  	return &Reconciler{
    41  		client:   client,
    42  		st:       st,
    43  		clock:    clock,
    44  		requeuer: indexer.NewRequeuer(),
    45  	}
    46  }
    47  
    48  func (r *Reconciler) Requeue() {
    49  	r.requeuer.Add(types.NamespacedName{Name: sessions.DefaultSessionName})
    50  }
    51  
    52  func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
    53  	session := &v1alpha1.Session{}
    54  	err := r.client.Get(ctx, req.NamespacedName, session)
    55  	if err != nil && !apierrors.IsNotFound(err) {
    56  		return ctrl.Result{}, fmt.Errorf("session reconcile: %v", err)
    57  	}
    58  
    59  	if apierrors.IsNotFound(err) || session.ObjectMeta.DeletionTimestamp != nil {
    60  		// NOTE(nick): This should never happen, and if it does, Tilt should
    61  		// immediately re-create the session.
    62  		return ctrl.Result{}, nil
    63  	}
    64  
    65  	return r.maybeUpdateObjectStatus(ctx, session)
    66  }
    67  
    68  // maybeUpdateObjectStatus builds the latest status for the Session and persists it.
    69  // Should only be called in the main reconciler thread.
    70  //
    71  // If the status has not changed since the last status update performed (by the
    72  // Reconciler), it will be skipped.
    73  //
    74  // Returns the latest object on success.
    75  func (r *Reconciler) maybeUpdateObjectStatus(ctx context.Context, session *v1alpha1.Session) (ctrl.Result, error) {
    76  	result := ctrl.Result{}
    77  	status := r.makeLatestStatus(session, &result)
    78  	if apicmp.DeepEqual(session.Status, status) {
    79  		// the status hasn't changed - avoid a spurious update
    80  		return result, nil
    81  	}
    82  
    83  	update := session.DeepCopy()
    84  	update.Status = status
    85  	err := r.client.Status().Update(ctx, update)
    86  	if err != nil {
    87  		return ctrl.Result{}, err
    88  	}
    89  	r.st.Dispatch(sessions.NewSessionStatusUpdateAction(update))
    90  	return result, nil
    91  }
    92  
    93  func (r *Reconciler) CreateBuilder(mgr ctrl.Manager) (*builder.Builder, error) {
    94  	b := ctrl.NewControllerManagedBy(mgr).
    95  		For(&v1alpha1.Session{}).
    96  		WatchesRawSource(r.requeuer)
    97  
    98  	return b, nil
    99  }