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 }