github.com/docker/compose-on-kubernetes@v0.5.0/cmd/e2e_benchmark/worker.go (about)

     1  package main
     2  
     3  import (
     4  	"fmt"
     5  	"sync"
     6  	"time"
     7  
     8  	clientset "github.com/docker/compose-on-kubernetes/api/client/clientset/typed/compose/v1beta2"
     9  	"github.com/docker/compose-on-kubernetes/api/compose/v1beta2"
    10  	coretypes "k8s.io/api/core/v1"
    11  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    12  	"k8s.io/apimachinery/pkg/runtime"
    13  	appsinformers "k8s.io/client-go/informers/apps/v1"
    14  	coreinformers "k8s.io/client-go/informers/core/v1"
    15  	k8sclientset "k8s.io/client-go/kubernetes"
    16  	"k8s.io/client-go/rest"
    17  	"k8s.io/client-go/tools/cache"
    18  )
    19  
    20  type stateUpdater func(*workerState)
    21  
    22  func benchmarkCreateStacks(stacksclient clientset.StacksGetter, workerID string, stackCount int) error {
    23  	for ix := 0; ix < stackCount; ix++ {
    24  		stack := &v1beta2.Stack{
    25  			ObjectMeta: metav1.ObjectMeta{
    26  				Name: fmt.Sprintf("stack-%d", ix),
    27  			},
    28  			Spec: &v1beta2.StackSpec{
    29  				Services: []v1beta2.ServiceConfig{
    30  					{
    31  						Name:  fmt.Sprintf("service-%d", ix),
    32  						Image: "nginx:1.15.3-alpine",
    33  					},
    34  				},
    35  			},
    36  		}
    37  		if _, err := stacksclient.Stacks(workerID).Create(stack); err != nil {
    38  			return err
    39  		}
    40  	}
    41  	return nil
    42  }
    43  
    44  func benchmarkWaitReconciliation(stacksclient clientset.StacksGetter, workerID string, stackCount int, updateState func(stateUpdater)) error {
    45  	var mut sync.Mutex
    46  	statuses := map[string]v1beta2.StackPhase{}
    47  	stopCh := make(chan struct{})
    48  	informer := cache.NewSharedInformer(&stackListerWatcher{stacksclient.Stacks(workerID)}, &v1beta2.Stack{}, 12*time.Hour)
    49  	informer.AddEventHandler(&cache.ResourceEventHandlerFuncs{
    50  		AddFunc: func(obj interface{}) {
    51  			s := obj.(*v1beta2.Stack)
    52  			if s.Status == nil {
    53  				return
    54  			}
    55  			mut.Lock()
    56  			defer mut.Unlock()
    57  			statuses[s.Name] = s.Status.Phase
    58  			if getStatusCounts(statuses).hasReconciliationEnded(stackCount) {
    59  				close(stopCh)
    60  			}
    61  		},
    62  		DeleteFunc: func(obj interface{}) {
    63  
    64  		},
    65  		UpdateFunc: func(_, obj interface{}) {
    66  			s := obj.(*v1beta2.Stack)
    67  			if s.Status == nil {
    68  				return
    69  			}
    70  			mut.Lock()
    71  			defer mut.Unlock()
    72  			statuses[s.Name] = s.Status.Phase
    73  			if getStatusCounts(statuses).hasReconciliationEnded(stackCount) {
    74  				close(stopCh)
    75  			}
    76  		},
    77  	})
    78  
    79  	go informer.Run(stopCh)
    80  
    81  	ticker := time.NewTicker(5 * time.Second)
    82  	defer ticker.Stop()
    83  
    84  	for {
    85  		select {
    86  		case <-stopCh:
    87  			return nil
    88  		case <-ticker.C:
    89  			func() {
    90  				mut.Lock()
    91  				defer mut.Unlock()
    92  
    93  				updateState(func(s *workerState) {
    94  					s.CurrentMessage = getStatusCounts(statuses).String()
    95  				})
    96  			}()
    97  		}
    98  	}
    99  }
   100  
   101  type stackListerWatcher struct {
   102  	clientset.StackInterface
   103  }
   104  
   105  var _ cache.ListerWatcher = &stackListerWatcher{}
   106  
   107  func (w *stackListerWatcher) List(options metav1.ListOptions) (runtime.Object, error) {
   108  	return w.StackInterface.List(options)
   109  }
   110  
   111  type statusCounts struct {
   112  	progressing int
   113  	failed      int
   114  	available   int
   115  }
   116  
   117  func (s statusCounts) hasReconciliationEnded(stackCount int) bool {
   118  	return s.failed+s.available == stackCount
   119  }
   120  
   121  func (s statusCounts) String() string {
   122  	return fmt.Sprintf("{available: %d, progressing: %d, failed: %d}", s.available, s.progressing, s.failed)
   123  }
   124  
   125  func getStatusCounts(src map[string]v1beta2.StackPhase) statusCounts {
   126  	revert := map[v1beta2.StackPhase]int{}
   127  	for _, v := range src {
   128  		revert[v]++
   129  	}
   130  	return statusCounts{
   131  		progressing: revert[v1beta2.StackProgressing],
   132  		failed:      revert[v1beta2.StackFailure],
   133  		available:   revert[v1beta2.StackAvailable],
   134  	}
   135  }
   136  
   137  func benchmarkDelete(stacksclient clientset.StacksGetter, k8sclient k8sclientset.Interface, workerID string, updateState func(stateUpdater)) error {
   138  	var mut sync.Mutex
   139  	closed := false
   140  	stopCh := make(chan struct{})
   141  	deploymentsInformer := appsinformers.NewDeploymentInformer(k8sclient, workerID, 12*time.Hour, cache.Indexers{})
   142  	servicesInformer := coreinformers.NewServiceInformer(k8sclient, workerID, 12*time.Hour, cache.Indexers{})
   143  	onDelete := func(_ interface{}) {
   144  		mut.Lock()
   145  		defer mut.Unlock()
   146  		if len(deploymentsInformer.GetStore().List()) == 0 &&
   147  			len(servicesInformer.GetStore().List()) == 0 &&
   148  			!closed {
   149  			closed = true
   150  			close(stopCh)
   151  		}
   152  	}
   153  	deploymentsInformer.AddEventHandler(&cache.ResourceEventHandlerFuncs{
   154  		AddFunc:    func(_ interface{}) {},
   155  		UpdateFunc: func(_, _ interface{}) {},
   156  		DeleteFunc: onDelete,
   157  	})
   158  	servicesInformer.AddEventHandler(&cache.ResourceEventHandlerFuncs{
   159  		AddFunc:    func(_ interface{}) {},
   160  		UpdateFunc: func(_, _ interface{}) {},
   161  		DeleteFunc: onDelete,
   162  	})
   163  	stopCh = make(chan struct{})
   164  	go deploymentsInformer.Run(stopCh)
   165  	go servicesInformer.Run(stopCh)
   166  	cache.WaitForCacheSync(stopCh, deploymentsInformer.HasSynced, servicesInformer.HasSynced)
   167  
   168  	if err := stacksclient.Stacks(workerID).DeleteCollection(nil, metav1.ListOptions{}); err != nil {
   169  		return err
   170  	}
   171  
   172  	updateState(func(s *workerState) {
   173  		s.CurrentMessage = "waiting for deployments and services to be removed"
   174  	})
   175  
   176  	ticker := time.NewTicker(5 * time.Second)
   177  	defer ticker.Stop()
   178  	for {
   179  		select {
   180  		case <-stopCh:
   181  			return nil
   182  		case <-ticker.C:
   183  			func() {
   184  				mut.Lock()
   185  				defer mut.Unlock()
   186  				updateState(func(s *workerState) {
   187  					s.CurrentMessage = fmt.Sprintf("remaining services: %d, deployments: %d", len(servicesInformer.GetStore().List()), len(deploymentsInformer.GetStore().List()))
   188  				})
   189  			}()
   190  		}
   191  	}
   192  }
   193  
   194  func benchmarkRun(cfg *rest.Config, workerID string, stackCount int, updateState func(stateUpdater)) error {
   195  	k8sclient, err := k8sclientset.NewForConfig(cfg)
   196  	if err != nil {
   197  		return err
   198  	}
   199  	// create namespace
   200  	_, err = k8sclient.CoreV1().Namespaces().Create(&coretypes.Namespace{
   201  		ObjectMeta: metav1.ObjectMeta{
   202  			Name: workerID,
   203  		},
   204  	})
   205  	if err != nil {
   206  		return err
   207  	}
   208  	defer k8sclient.CoreV1().Namespaces().Delete(workerID, nil)
   209  	stacksclient, err := clientset.NewForConfig(cfg)
   210  	if err != nil {
   211  		return err
   212  	}
   213  
   214  	updateState(func(s *workerState) {
   215  		s.CurrentPhase = "stacks creation"
   216  		s.CurrentMessage = fmt.Sprintf("creating %d stacks", stackCount)
   217  	})
   218  	if err := benchmarkCreateStacks(stacksclient, workerID, stackCount); err != nil {
   219  		return err
   220  	}
   221  	updateState(func(s *workerState) {
   222  		s.PreviousPhases = append(s.PreviousPhases, phaseState{
   223  			DoneTime: time.Now(),
   224  			Name:     "stacks creation",
   225  		})
   226  		s.CurrentPhase = "reconciliation"
   227  		s.CurrentMessage = "waiting for reconciliation..."
   228  	})
   229  	if err := benchmarkWaitReconciliation(stacksclient, workerID, stackCount, updateState); err != nil {
   230  		return err
   231  	}
   232  	updateState(func(s *workerState) {
   233  		s.PreviousPhases = append(s.PreviousPhases, phaseState{
   234  			DoneTime: time.Now(),
   235  			Name:     "reconciliation",
   236  		})
   237  		s.CurrentPhase = "delete"
   238  		s.CurrentMessage = "deleting stacks..."
   239  	})
   240  	if err := benchmarkDelete(stacksclient, k8sclient, workerID, updateState); err != nil {
   241  		return err
   242  	}
   243  	updateState(func(s *workerState) {
   244  		s.PreviousPhases = append(s.PreviousPhases, phaseState{
   245  			DoneTime: time.Now(),
   246  			Name:     "delete",
   247  		})
   248  		s.CurrentPhase = "done"
   249  		s.CurrentMessage = ""
   250  	})
   251  	return nil
   252  }