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  }