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 }