github.com/cilium/cilium@v1.16.2/pkg/bgpv1/manager/state_tracker.go (about)

     1  // SPDX-License-Identifier: Apache-2.0
     2  // Copyright Authors of Cilium
     3  
     4  package manager
     5  
     6  import (
     7  	"context"
     8  	"errors"
     9  
    10  	"github.com/cilium/cilium/pkg/bgpv1/manager/reconcilerv2"
    11  	"github.com/cilium/cilium/pkg/bgpv1/types"
    12  )
    13  
    14  var (
    15  	ErrInstanceDoesNotExist = errors.New("instance does not exist")
    16  )
    17  
    18  // trackInstanceStateChange should be started as a goroutine. It listens on the tracker channel
    19  // and signals state reconciler. It will be returned when tracker go routine is closed.
    20  func (m *BGPRouterManager) trackInstanceStateChange(instance string, tracker chan struct{}) {
    21  	for range tracker {
    22  		m.Logger.WithField(types.InstanceLogField, instance).Debug("Event change detected for instance")
    23  
    24  		// insert this instance in pending state modified list
    25  		// we can be waiting here for long since lock is also taken by main reconcile loop.
    26  		// We do not want to modify PendingInstances set while main reconcile loop is processing it.
    27  		// Any new events will level trigger tracker channel and move along.
    28  		m.state.pendingInstancesMutex.Lock()
    29  		m.state.pendingInstances.Insert(instance)
    30  		m.state.pendingInstancesMutex.Unlock()
    31  
    32  		// notify the main reconcile loop that the state for some instance has changed
    33  		// It is okay if that channel is full, there can be multiple instances trying to
    34  		// notify the main reconcile loop that the state has changed.
    35  		select {
    36  		case m.state.reconcileSignal <- struct{}{}:
    37  		default:
    38  		}
    39  	}
    40  
    41  	// tracker is closed, signal the main reconcile loop that this instance is deleted so it
    42  	// can do any necessary cleanup.
    43  	m.state.instanceDeletionSignal <- instance
    44  	m.Logger.WithField(types.InstanceLogField, instance).Debug("Instance deleted, stopping state tracker")
    45  }
    46  
    47  // reconcileState is the main loop that reconciles the state of all instances that have pending state changes.
    48  // It will take StateMutex lock to process the pending instances and remove them from the pending instances set.
    49  // Any new state changes will be blocked till this method completes.
    50  func (m *BGPRouterManager) reconcileState(ctx context.Context) error {
    51  	var allErrs error
    52  
    53  	// we lock the state mutex so no changes to PendingInstances set can be made while we are processing it.
    54  	m.state.pendingInstancesMutex.Lock()
    55  	defer m.state.pendingInstancesMutex.Unlock()
    56  
    57  	m.Logger.WithField("UpdatedInstances", m.state.pendingInstances.Len()).
    58  		Debug("Reconciling state for instances with pending state changes")
    59  
    60  	// process all pending instances, failed instances will be retried in next reconcile loop.
    61  	for instanceName := range m.state.pendingInstances {
    62  		err := m.reconcileInstanceState(ctx, instanceName)
    63  		if err != nil && !errors.Is(err, ErrInstanceDoesNotExist) {
    64  			allErrs = errors.Join(allErrs, err)
    65  		} else {
    66  			m.state.pendingInstances.Delete(instanceName)
    67  		}
    68  	}
    69  
    70  	return allErrs
    71  }
    72  
    73  func (m *BGPRouterManager) reconcileInstanceDeletion(ctx context.Context, instanceName string) {
    74  	m.RLock()
    75  	defer m.RUnlock()
    76  
    77  	for _, stateReconciler := range m.state.reconcilers {
    78  		err := stateReconciler.Reconcile(ctx, reconcilerv2.StateReconcileParams{
    79  			ConfigMode:      m.ConfigMode,
    80  			DeletedInstance: instanceName,
    81  		})
    82  		if err != nil {
    83  			m.Logger.WithError(err).
    84  				WithField(types.InstanceLogField, instanceName).
    85  				Error("Error while reconciling state")
    86  		}
    87  	}
    88  }
    89  
    90  // reconcileInstanceState reconciles the state of a single instance. It will take read lock
    91  // on BGPInstances lock as we are inspecting BGP instances.
    92  // It will call all the state reconcilers to reconcile the state of the instance.
    93  func (m *BGPRouterManager) reconcileInstanceState(ctx context.Context, instanceName string) error {
    94  	m.RLock()
    95  	defer m.RUnlock()
    96  
    97  	instance, exists := m.BGPInstances[instanceName]
    98  	if !exists {
    99  		return ErrInstanceDoesNotExist
   100  	}
   101  
   102  	var err error
   103  	for _, stateReconciler := range m.state.reconcilers {
   104  		err = errors.Join(err, stateReconciler.Reconcile(ctx, reconcilerv2.StateReconcileParams{
   105  			ConfigMode:      m.ConfigMode,
   106  			UpdatedInstance: instance,
   107  		}))
   108  	}
   109  
   110  	return err
   111  }