github.com/aporeto-inc/trireme-lib@v10.358.0+incompatible/monitor/internal/pod/resync.go (about)

     1  // +build linux !windows
     2  
     3  package podmonitor
     4  
     5  import (
     6  	"context"
     7  	"errors"
     8  	"fmt"
     9  	"sync"
    10  	"time"
    11  
    12  	corev1 "k8s.io/api/core/v1"
    13  
    14  	"sigs.k8s.io/controller-runtime/pkg/client"
    15  	"sigs.k8s.io/controller-runtime/pkg/event"
    16  
    17  	"go.uber.org/zap"
    18  )
    19  
    20  // ResyncWithAllPods is called from the implemented resync, it will list all pods
    21  // and fire them down the event source (the generic event channel).
    22  // It will block until every pod at the time of calling has been calling `Reconcile` at least once.
    23  func ResyncWithAllPods(ctx context.Context, c client.Client, i *ResyncInfoChan, evCh chan<- event.GenericEvent, nodeName string) error {
    24  	zap.L().Debug("Pod resync: starting to resync all pods")
    25  	if c == nil {
    26  		return errors.New("pod: no client available")
    27  	}
    28  
    29  	if evCh == nil {
    30  		return errors.New("pod: no event source available")
    31  	}
    32  
    33  	if i == nil {
    34  		return errors.New("pod: no resync info channel available")
    35  	}
    36  
    37  	list := &corev1.PodList{}
    38  	if err := c.List(ctx, &client.ListOptions{}, list); err != nil {
    39  		return fmt.Errorf("pod: %s", err.Error())
    40  	}
    41  
    42  	// build a map of pods that we will expect to turn true
    43  	m := make(map[string]bool)
    44  	for _, pod := range list.Items {
    45  		if pod.Spec.NodeName != nodeName {
    46  			continue
    47  		}
    48  		podName := pod.GetName()
    49  		podNamespace := pod.GetNamespace()
    50  		if podName != "" && podNamespace != "" {
    51  			m[fmt.Sprintf("%s/%s", podNamespace, podName)] = false
    52  		}
    53  	}
    54  	zap.L().Debug("Pod resync: pods that need to be resynced", zap.Any("pods", m))
    55  
    56  	// Request that the controller reports to us from now on
    57  	i.EnableNeedsInfo()
    58  
    59  	// fire away events to the controller
    60  	for _, pod := range list.Items {
    61  		if pod.Spec.NodeName != nodeName {
    62  			continue
    63  		}
    64  		p := pod.DeepCopy()
    65  		evCh <- event.GenericEvent{
    66  			Meta:   p.GetObjectMeta(),
    67  			Object: p,
    68  		}
    69  	}
    70  
    71  	// now wait for all pods to have reported back
    72  	begin := time.Now()
    73  waitLoop:
    74  	for {
    75  		if time.Since(begin) > (time.Second * 60) {
    76  			zap.L().Warn("Pod resync: failed to reconcile on all pods. Unblocking now anyway.")
    77  			break waitLoop
    78  		}
    79  
    80  		select {
    81  		case info := <-*i.GetInfoCh():
    82  			if _, ok := m[info]; ok {
    83  				zap.L().Debug("Pod resync: pod that is part of the resync", zap.String("pod", info))
    84  				m[info] = true
    85  			} else {
    86  				zap.L().Debug("Pod resync: *not* a pod that is part of the resync", zap.String("pod", info))
    87  			}
    88  		case <-time.After(time.Second * 5):
    89  			zap.L().Debug("Pod resync: timeout waiting for pod reconcile")
    90  		}
    91  
    92  		// now check if we can abort already
    93  		for _, v := range m {
    94  			if !v {
    95  				continue waitLoop
    96  			}
    97  		}
    98  		break waitLoop
    99  	}
   100  	i.DisableNeedsInfo()
   101  	zap.L().Debug("Pod resync: finished resyncing all pods")
   102  
   103  	return nil
   104  }
   105  
   106  // ResyncInfoChan is used to report back from the controller on which pods it has processed.
   107  // It allows the Resync of the monitor to block and wait until a list has been processed.
   108  type ResyncInfoChan struct {
   109  	m  sync.RWMutex
   110  	b  bool
   111  	ch chan string
   112  }
   113  
   114  // NewResyncInfoChan creates a new ResyncInfoChan
   115  func NewResyncInfoChan() *ResyncInfoChan {
   116  	return &ResyncInfoChan{
   117  		ch: make(chan string, 100),
   118  	}
   119  }
   120  
   121  // EnableNeedsInfo enables the need for sending info
   122  func (r *ResyncInfoChan) EnableNeedsInfo() {
   123  	r.m.Lock()
   124  	defer r.m.Unlock()
   125  	r.b = true
   126  }
   127  
   128  // DisableNeedsInfo disables the need for sending info
   129  func (r *ResyncInfoChan) DisableNeedsInfo() {
   130  	r.m.Lock()
   131  	defer r.m.Unlock()
   132  	r.b = false
   133  }
   134  
   135  // NeedsInfo returns if there is a need for sending info
   136  func (r *ResyncInfoChan) NeedsInfo() bool {
   137  	r.m.RLock()
   138  	defer r.m.RUnlock()
   139  	return r.b
   140  }
   141  
   142  // SendInfo will make the info available through an internal channel
   143  func (r *ResyncInfoChan) SendInfo(info string) {
   144  	r.m.RLock()
   145  	defer r.m.RUnlock()
   146  	if r.b {
   147  		r.ch <- info
   148  	}
   149  }
   150  
   151  // GetInfoCh returns the channel
   152  func (r *ResyncInfoChan) GetInfoCh() *chan string {
   153  	r.m.RLock()
   154  	defer r.m.RUnlock()
   155  	return &r.ch
   156  }