github.com/docker/compose-on-kubernetes@v0.5.0/internal/controller/childrenstore.go (about) 1 package controller 2 3 import ( 4 "math/rand" 5 "time" 6 7 "github.com/docker/compose-on-kubernetes/api/labels" 8 "github.com/docker/compose-on-kubernetes/internal/stackresources" 9 log "github.com/sirupsen/logrus" 10 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 11 appsinformers "k8s.io/client-go/informers/apps/v1" 12 coreinformers "k8s.io/client-go/informers/core/v1" 13 k8sclientset "k8s.io/client-go/kubernetes" 14 "k8s.io/client-go/tools/cache" 15 ) 16 17 // ChildrenListener listens to changes from resources created for a stack 18 type ChildrenListener struct { 19 deployments cache.Indexer 20 statefulsets cache.Indexer 21 daemonsets cache.Indexer 22 services cache.Indexer 23 reconcileQueue chan<- string 24 runFunc func(stop chan struct{}) 25 hasSynced func() bool 26 } 27 28 func (s *ChildrenListener) onEvent(obj interface{}, methodName string) { 29 if !s.hasSynced() { 30 return 31 } 32 n, err := extractStackNameAndNamespace(obj) 33 if err != nil { 34 log.Warnf("ChildrenListener: %s: %s. obj was %#v", methodName, err, obj) 35 return 36 } 37 objKey := n.objKey() 38 log.Debugf("Sending stack reconciliation request: %s", objKey) 39 s.reconcileQueue <- objKey 40 } 41 42 func (s *ChildrenListener) onAdd(obj interface{}) { 43 s.onEvent(obj, "onAdd") 44 } 45 46 func (s *ChildrenListener) onUpdate(_, newObj interface{}) { 47 // first argument is to satisfy cache.ResourceEventHandlerFuncs 48 s.onEvent(newObj, "onUpdate") 49 } 50 51 func (s *ChildrenListener) onDelete(obj interface{}) { 52 if tombstone, ok := obj.(cache.DeletedFinalStateUnknown); ok { 53 obj = tombstone.Obj 54 } 55 s.onEvent(obj, "onDelete") 56 } 57 58 func (s *ChildrenListener) getCurrentStackState(objKey string) (*stackresources.StackState, error) { 59 deployments, err := s.deployments.ByIndex("by-stack", objKey) 60 if err != nil { 61 return nil, err 62 } 63 statefulsets, err := s.statefulsets.ByIndex("by-stack", objKey) 64 if err != nil { 65 return nil, err 66 } 67 daemonsets, err := s.daemonsets.ByIndex("by-stack", objKey) 68 if err != nil { 69 return nil, err 70 } 71 services, err := s.services.ByIndex("by-stack", objKey) 72 if err != nil { 73 return nil, err 74 } 75 return stackresources.NewStackState(append(deployments, append(statefulsets, append(daemonsets, services...)...)...)...) 76 } 77 78 // StartAndWaitForFullSync starts the underlying informers and wait for a full sync to occur (makes sure everything is in cache) 79 func (s *ChildrenListener) StartAndWaitForFullSync(stop chan struct{}) bool { 80 s.runFunc(stop) 81 return cache.WaitForCacheSync(stop, s.hasSynced) 82 } 83 84 func randomDuration(baseDuration time.Duration) time.Duration { 85 var baseSeconds = baseDuration.Seconds() 86 factor := rand.Float64() + 1 // number between 1 on 2 87 finalSeconds := baseSeconds * factor 88 return time.Duration(float64(time.Second) * finalSeconds) 89 } 90 91 // NewChildrenListener creates a ChildrenListener 92 func NewChildrenListener(clientSet k8sclientset.Interface, reconciliationInterval time.Duration, reconcileQueue chan<- string) (*ChildrenListener, error) { 93 sharedInformersOption := func(o *metav1.ListOptions) { 94 o.LabelSelector = labels.ForStackName 95 } 96 97 indexers := cache.Indexers{ 98 "by-stack": byStackIndexer, 99 } 100 deploymentsInformer := appsinformers.NewFilteredDeploymentInformer(clientSet, metav1.NamespaceAll, randomDuration(reconciliationInterval), indexers, sharedInformersOption) 101 statefulsetInformer := appsinformers.NewFilteredStatefulSetInformer(clientSet, metav1.NamespaceAll, randomDuration(reconciliationInterval), indexers, sharedInformersOption) 102 daemonsetInformer := appsinformers.NewFilteredDaemonSetInformer(clientSet, metav1.NamespaceAll, randomDuration(reconciliationInterval), indexers, sharedInformersOption) 103 servicesInformer := coreinformers.NewFilteredServiceInformer(clientSet, metav1.NamespaceAll, randomDuration(reconciliationInterval), indexers, sharedInformersOption) 104 result := &ChildrenListener{ 105 deployments: deploymentsInformer.GetIndexer(), 106 statefulsets: statefulsetInformer.GetIndexer(), 107 daemonsets: daemonsetInformer.GetIndexer(), 108 services: servicesInformer.GetIndexer(), 109 reconcileQueue: reconcileQueue, 110 runFunc: func(stop chan struct{}) { 111 go deploymentsInformer.Run(stop) 112 go statefulsetInformer.Run(stop) 113 go daemonsetInformer.Run(stop) 114 go servicesInformer.Run(stop) 115 }, 116 hasSynced: func() bool { 117 return deploymentsInformer.HasSynced() && 118 statefulsetInformer.HasSynced() && 119 daemonsetInformer.HasSynced() && 120 servicesInformer.HasSynced() 121 }, 122 } 123 prepareResourceInformers(cache.ResourceEventHandlerFuncs{ 124 AddFunc: result.onAdd, 125 DeleteFunc: result.onDelete, 126 UpdateFunc: result.onUpdate, 127 }, deploymentsInformer, statefulsetInformer, daemonsetInformer, servicesInformer) 128 129 return result, nil 130 } 131 132 func prepareResourceInformers(resourceHandler cache.ResourceEventHandler, informers ...cache.SharedIndexInformer) { 133 for _, i := range informers { 134 i.AddEventHandler(resourceHandler) 135 } 136 }