You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

objectsearchservice.go 20 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623
  1. /*
  2. Copyright 2021 The KubeEdge Authors.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package objectsearch
  14. import (
  15. "context"
  16. "fmt"
  17. "time"
  18. appsv1 "k8s.io/api/apps/v1"
  19. v1 "k8s.io/api/core/v1"
  20. "k8s.io/apimachinery/pkg/api/errors"
  21. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  22. utilrand "k8s.io/apimachinery/pkg/util/rand"
  23. utilruntime "k8s.io/apimachinery/pkg/util/runtime"
  24. "k8s.io/apimachinery/pkg/util/wait"
  25. "k8s.io/client-go/kubernetes"
  26. "k8s.io/client-go/kubernetes/scheme"
  27. v1core "k8s.io/client-go/kubernetes/typed/core/v1"
  28. appslisters "k8s.io/client-go/listers/apps/v1"
  29. corelisters "k8s.io/client-go/listers/core/v1"
  30. "k8s.io/client-go/tools/cache"
  31. "k8s.io/client-go/tools/record"
  32. "k8s.io/client-go/util/workqueue"
  33. "k8s.io/klog/v2"
  34. sednav1 "github.com/kubeedge/sedna/pkg/apis/sedna/v1alpha1"
  35. sednaclientset "github.com/kubeedge/sedna/pkg/client/clientset/versioned/typed/sedna/v1alpha1"
  36. sednav1listers "github.com/kubeedge/sedna/pkg/client/listers/sedna/v1alpha1"
  37. "github.com/kubeedge/sedna/pkg/globalmanager/config"
  38. "github.com/kubeedge/sedna/pkg/globalmanager/runtime"
  39. )
  40. const (
  41. // Name is this controller name
  42. Name = "ObjectSearch"
  43. // KindName is the kind name of CR this controller controls
  44. KindName = "ObjectSearchService"
  45. )
  46. const (
  47. objectSearchUserWorker = "userworker"
  48. objectSearchTrackingWorker = "trackingworker"
  49. objectSearchReidWorker = "reidworker"
  50. reidServicePort = 9378
  51. userWorkerPort = 9379
  52. )
  53. // Kind contains the schema.GroupVersionKind for this controller type.
  54. var Kind = sednav1.SchemeGroupVersion.WithKind(KindName)
  55. // Controller ensures that all ObjectSearchService objects
  56. // have corresponding pods to run their configured workload.
  57. type Controller struct {
  58. kubeClient kubernetes.Interface
  59. client sednaclientset.SednaV1alpha1Interface
  60. // podStoreSynced returns true if the pod store has been synced at least once.
  61. podStoreSynced cache.InformerSynced
  62. // A store of pods
  63. podStore corelisters.PodLister
  64. // deploymentsSynced returns true if the deployment store has been synced at least once.
  65. deploymentsSynced cache.InformerSynced
  66. // A store of deployment
  67. deploymentsLister appslisters.DeploymentLister
  68. // serviceStoreSynced returns true if the ObjectSearchService store has been synced at least once.
  69. serviceStoreSynced cache.InformerSynced
  70. // A store of service
  71. serviceLister sednav1listers.ObjectSearchServiceLister
  72. // ObjectSearchServices that need to be updated
  73. queue workqueue.RateLimitingInterface
  74. recorder record.EventRecorder
  75. cfg *config.ControllerConfig
  76. sendToEdgeFunc runtime.DownstreamSendFunc
  77. }
  78. // Run starts the main goroutine responsible for watching and syncing services.
  79. func (c *Controller) Run(stopCh <-chan struct{}) {
  80. workers := 1
  81. defer utilruntime.HandleCrash()
  82. defer c.queue.ShutDown()
  83. klog.Infof("Starting %s controller", Name)
  84. defer klog.Infof("Shutting down %s controller", Name)
  85. if !cache.WaitForNamedCacheSync(Name, stopCh, c.podStoreSynced, c.serviceStoreSynced) {
  86. klog.Errorf("failed to wait for %s caches to sync", Name)
  87. return
  88. }
  89. klog.Infof("Starting %s workers", Name)
  90. for i := 0; i < workers; i++ {
  91. go wait.Until(c.worker, time.Second, stopCh)
  92. }
  93. <-stopCh
  94. }
  95. // enqueueByPod enqueues the ObjectSearchService object of the specified pod.
  96. func (c *Controller) enqueueByPod(pod *v1.Pod, immediate bool) {
  97. controllerRef := metav1.GetControllerOf(pod)
  98. if controllerRef == nil {
  99. return
  100. }
  101. if controllerRef.Kind != Kind.Kind {
  102. return
  103. }
  104. service, err := c.serviceLister.ObjectSearchServices(pod.Namespace).Get(controllerRef.Name)
  105. if err != nil {
  106. return
  107. }
  108. if service.UID != controllerRef.UID {
  109. return
  110. }
  111. c.enqueueController(service, immediate)
  112. }
  113. // When a pod is created, enqueue the controller that manages it and update it's expectations.
  114. func (c *Controller) addPod(obj interface{}) {
  115. pod := obj.(*v1.Pod)
  116. if pod.DeletionTimestamp != nil {
  117. // on a restart of the controller, it's possible a new pod shows up in a state that
  118. // is already pending deletion. Prevent the pod from being a creation observation.
  119. c.deletePod(pod)
  120. return
  121. }
  122. // backoff to queue when PodFailed
  123. immediate := pod.Status.Phase != v1.PodFailed
  124. c.enqueueByPod(pod, immediate)
  125. }
  126. // When a pod is updated, figure out what object search service manage it and wake them up.
  127. func (c *Controller) updatePod(old, cur interface{}) {
  128. curPod := cur.(*v1.Pod)
  129. oldPod := old.(*v1.Pod)
  130. // no pod update, no queue
  131. if curPod.ResourceVersion == oldPod.ResourceVersion {
  132. return
  133. }
  134. c.addPod(curPod)
  135. }
  136. // deletePod enqueues the ObjectSearchService obj When a pod is deleted
  137. func (c *Controller) deletePod(obj interface{}) {
  138. pod, ok := obj.(*v1.Pod)
  139. // comment from https://github.com/kubernetes/kubernetes/blob/master/pkg/controller/job/job_controller.go
  140. // When a delete is dropped, the relist will notice a pod in the store not
  141. // in the list, leading to the insertion of a tombstone object which contains
  142. // the deleted key/value. Note that this value might be stale. If the pod
  143. // changed labels the new ObjectSearchService will not be woken up till the periodic resync.
  144. if !ok {
  145. tombstone, ok := obj.(cache.DeletedFinalStateUnknown)
  146. if !ok {
  147. klog.Warningf("couldn't get object from tombstone %+v", obj)
  148. return
  149. }
  150. pod, ok = tombstone.Obj.(*v1.Pod)
  151. if !ok {
  152. klog.Warningf("tombstone contained object that is not a pod %+v", obj)
  153. return
  154. }
  155. }
  156. c.enqueueByPod(pod, true)
  157. }
  158. // enqueueByDeployment enqueues the ObjectSearchService object of the specified deployment.
  159. func (c *Controller) enqueueByDeployment(deployment *appsv1.Deployment, immediate bool) {
  160. controllerRef := metav1.GetControllerOf(deployment)
  161. if controllerRef == nil {
  162. return
  163. }
  164. if controllerRef.Kind != Kind.Kind {
  165. return
  166. }
  167. service, err := c.serviceLister.ObjectSearchServices(deployment.Namespace).Get(controllerRef.Name)
  168. if err != nil {
  169. return
  170. }
  171. if service.UID != controllerRef.UID {
  172. return
  173. }
  174. c.enqueueController(service, immediate)
  175. }
  176. // When a deployment is created, enqueue the controller that manages it and update it's expectations.
  177. func (c *Controller) addDeployment(obj interface{}) {
  178. deployment := obj.(*appsv1.Deployment)
  179. c.enqueueByDeployment(deployment, true)
  180. }
  181. // deleteDeployment enqueues the ObjectSearchService obj When a deleteDeployment is deleted
  182. func (c *Controller) deleteDeployment(obj interface{}) {
  183. deployment, ok := obj.(*appsv1.Deployment)
  184. // comment from https://github.com/kubernetes/kubernetes/blob/master/pkg/controller/deployment/deployment_controller.go
  185. if !ok {
  186. tombstone, ok := obj.(cache.DeletedFinalStateUnknown)
  187. if !ok {
  188. klog.Warningf("couldn't get object from tombstone %+v", obj)
  189. return
  190. }
  191. deployment, ok = tombstone.Obj.(*appsv1.Deployment)
  192. if !ok {
  193. klog.Warningf("tombstone contained object that is not a Deployment %+v", obj)
  194. return
  195. }
  196. }
  197. c.enqueueByDeployment(deployment, true)
  198. }
  199. // When a deployment is updated, figure out what object search service manage it and wake them up.
  200. func (c *Controller) updateDeployment(old, cur interface{}) {
  201. oldD := old.(*appsv1.Deployment)
  202. curD := cur.(*appsv1.Deployment)
  203. // no deployment update, no queue
  204. if curD.ResourceVersion == oldD.ResourceVersion {
  205. return
  206. }
  207. c.addDeployment(curD)
  208. }
  209. // obj could be an *sednav1.ObjectSearchService, or a DeletionFinalStateUnknown marker item,
  210. // immediate tells the controller to update the status right away, and should
  211. // happen ONLY when there was a successful pod run.
  212. func (c *Controller) enqueueController(obj interface{}, immediate bool) {
  213. key, err := cache.DeletionHandlingMetaNamespaceKeyFunc(obj)
  214. if err != nil {
  215. klog.Warningf("Couldn't get key for object %+v: %v", obj, err)
  216. return
  217. }
  218. backoff := time.Duration(0)
  219. if !immediate {
  220. backoff = runtime.GetBackoff(c.queue, key)
  221. }
  222. c.queue.AddAfter(key, backoff)
  223. }
  224. // worker runs a worker thread that just dequeues items, processes them, and marks them done.
  225. // It enforces that the sync is never invoked concurrently with the same key.
  226. func (c *Controller) worker() {
  227. for c.processNextWorkItem() {
  228. }
  229. }
  230. func (c *Controller) processNextWorkItem() bool {
  231. key, quit := c.queue.Get()
  232. if quit {
  233. return false
  234. }
  235. defer c.queue.Done(key)
  236. forget, err := c.sync(key.(string))
  237. if err == nil {
  238. if forget {
  239. c.queue.Forget(key)
  240. }
  241. return true
  242. }
  243. klog.Warningf("Error syncing objectsearch service: %v", err)
  244. c.queue.AddRateLimited(key)
  245. return true
  246. }
  247. // sync will sync the objectsearchservice with the given key.
  248. // This function is not meant to be invoked concurrently with the same key.
  249. func (c *Controller) sync(key string) (bool, error) {
  250. startTime := time.Now()
  251. defer func() {
  252. klog.V(4).Infof("Finished syncing objectsearch service %q (%v)", key, time.Since(startTime))
  253. }()
  254. ns, name, err := cache.SplitMetaNamespaceKey(key)
  255. if err != nil {
  256. return false, err
  257. }
  258. if len(ns) == 0 || len(name) == 0 {
  259. return false, fmt.Errorf("invalid objectsearch service key %q: either namespace or name is missing", key)
  260. }
  261. sharedService, err := c.serviceLister.ObjectSearchServices(ns).Get(name)
  262. if err != nil {
  263. if errors.IsNotFound(err) {
  264. klog.V(4).Infof("ObjectSearchService has been deleted: %v", key)
  265. return true, nil
  266. }
  267. return false, err
  268. }
  269. service := *sharedService
  270. // if service was finished previously, we don't want to redo the termination.
  271. if isServiceFinished(&service) {
  272. return true, nil
  273. }
  274. // set kind for service in case that the kind is None.
  275. // more details at https://github.com/kubernetes/kubernetes/issues/3030
  276. service.SetGroupVersionKind(Kind)
  277. selectorPods, _ := runtime.GenerateWorkerSelector(&service, objectSearchTrackingWorker)
  278. selectorDeployments, _ := runtime.GenerateSelector(&service)
  279. pods, err := c.podStore.Pods(service.Namespace).List(selectorPods)
  280. deployments, err := c.deploymentsLister.Deployments(service.Namespace).List(selectorDeployments)
  281. if err != nil {
  282. return false, err
  283. }
  284. latestConditionLen := len(service.Status.Conditions)
  285. var podFailed int32 = 0
  286. var deploymentFailed int32 = 0
  287. // neededPodCounts indicates the num of tracking worker pods should be created successfully in a objectsearch service currently.
  288. // neededDeploymentCounts indicates the num of deployments should be created successfully in a objectsearch service currently,
  289. // and one deployment is for userWorker and the other deployment is for reidWorkers.
  290. var neededPodCounts = int32(len(service.Spec.TrackingWorkers))
  291. var neededDeploymentCounts int32 = 2
  292. activePods := runtime.CalcActivePodCount(pods)
  293. activeDeployments := runtime.CalcActiveDeploymentCount(deployments)
  294. if service.Status.StartTime == nil {
  295. now := metav1.Now()
  296. service.Status.StartTime = &now
  297. } else {
  298. podFailed = neededPodCounts - activePods
  299. deploymentFailed = neededDeploymentCounts - activeDeployments
  300. }
  301. var manageServiceErr error
  302. serviceFailed := false
  303. var latestConditionType sednav1.ObjectSearchServiceConditionType = ""
  304. // get the latest condition type
  305. // based on that condition updated is appended, not inserted.
  306. jobConditions := service.Status.Conditions
  307. if len(jobConditions) > 0 {
  308. latestConditionType = (jobConditions)[len(jobConditions)-1].Type
  309. }
  310. var newCondtionType sednav1.ObjectSearchServiceConditionType
  311. var reason string
  312. var message string
  313. switch {
  314. case podFailed > 0:
  315. serviceFailed = true
  316. reason = "podFailed"
  317. message = "the worker of service failed"
  318. newCondtionType = sednav1.ObjectSearchServiceCondFailed
  319. c.recorder.Event(&service, v1.EventTypeWarning, reason, message)
  320. case deploymentFailed > 0:
  321. serviceFailed = true
  322. reason = "deploymentFailed"
  323. message = "the worker of service failed"
  324. newCondtionType = sednav1.ObjectSearchServiceCondFailed
  325. c.recorder.Event(&service, v1.EventTypeWarning, reason, message)
  326. default:
  327. if len(pods) == 0 && len(deployments) == 0 {
  328. activePods, activeDeployments, manageServiceErr = c.createWorkers(&service)
  329. }
  330. if manageServiceErr != nil {
  331. klog.V(2).Infof("failed to create worker: %v", manageServiceErr)
  332. serviceFailed = true
  333. message = error.Error(manageServiceErr)
  334. newCondtionType = sednav1.ObjectSearchServiceCondFailed
  335. podFailed = neededPodCounts - activePods
  336. deploymentFailed = neededDeploymentCounts - activeDeployments
  337. } else {
  338. // TODO: handle the case that the pod phase is PodSucceeded
  339. newCondtionType = sednav1.ObjectSearchServiceCondRunning
  340. }
  341. }
  342. if newCondtionType != latestConditionType {
  343. service.Status.Conditions = append(service.Status.Conditions, newServiceCondition(newCondtionType, reason, message))
  344. }
  345. forget := false
  346. // calculate the number of active pods and deployments
  347. active := activePods + activeDeployments
  348. failed := podFailed + deploymentFailed
  349. // no need to update the objectsearchservice if the status hasn't changed since last time
  350. if service.Status.Active != active || service.Status.Failed != failed || len(service.Status.Conditions) != latestConditionLen {
  351. service.Status.Active = active
  352. service.Status.Failed = failed
  353. if err := c.updateStatus(&service); err != nil {
  354. return forget, err
  355. }
  356. if serviceFailed && !isServiceFinished(&service) {
  357. // returning an error will re-enqueue objectsearchservice after the backoff period
  358. return forget, fmt.Errorf("failed pod(s) detected for objectsearch service key %q", key)
  359. }
  360. forget = true
  361. }
  362. return forget, manageServiceErr
  363. }
  364. // newServiceCondition creates a new condition
  365. func newServiceCondition(conditionType sednav1.ObjectSearchServiceConditionType, reason, message string) sednav1.ObjectSearchServiceCondition {
  366. return sednav1.ObjectSearchServiceCondition{
  367. Type: conditionType,
  368. Status: v1.ConditionTrue,
  369. LastHeartbeatTime: metav1.Now(),
  370. LastTransitionTime: metav1.Now(),
  371. Reason: reason,
  372. Message: message,
  373. }
  374. }
  375. // countPods returns number of succeeded and failed pods
  376. func countPods(pods []*v1.Pod) (failed int32) {
  377. failed = int32(filterPods(pods, v1.PodFailed))
  378. return
  379. }
  380. // filterPods returns pods based on their phase.
  381. func filterPods(pods []*v1.Pod, phase v1.PodPhase) int {
  382. result := 0
  383. for i := range pods {
  384. if phase == pods[i].Status.Phase {
  385. result++
  386. }
  387. }
  388. return result
  389. }
  390. func (c *Controller) updateStatus(service *sednav1.ObjectSearchService) error {
  391. client := c.client.ObjectSearchServices(service.Namespace)
  392. return runtime.RetryUpdateStatus(service.Name, service.Namespace, func() error {
  393. newService, err := client.Get(context.TODO(), service.Name, metav1.GetOptions{})
  394. if err != nil {
  395. return err
  396. }
  397. newService.Status = service.Status
  398. _, err = client.UpdateStatus(context.TODO(), newService, metav1.UpdateOptions{})
  399. return err
  400. })
  401. }
  402. func isServiceFinished(j *sednav1.ObjectSearchService) bool {
  403. for _, c := range j.Status.Conditions {
  404. if (c.Type == sednav1.ObjectSearchServiceCondFailed) && c.Status == v1.ConditionTrue {
  405. return true
  406. }
  407. }
  408. return false
  409. }
  410. func (c *Controller) createWorkers(service *sednav1.ObjectSearchService) (activePods int32, activeDeployments int32, err error) {
  411. activePods = 0
  412. activeDeployments = 0
  413. // create reid worker deployment
  414. var reidWorkerParam runtime.WorkerParam
  415. reidWorkerParam.WorkerType = objectSearchReidWorker
  416. _, err = runtime.CreateDeploymentWithTemplate(c.kubeClient, service, &service.Spec.ReidWorkers.DeploymentSpec, &reidWorkerParam)
  417. if err != nil {
  418. return activePods, activeDeployments, fmt.Errorf("failed to create reid worker deployment: %w", err)
  419. }
  420. activeDeployments++
  421. // create reid worker edgemesh service
  422. reidServiceHost, err := runtime.CreateEdgeMeshService(c.kubeClient, service, objectSearchReidWorker, reidServicePort)
  423. if err != nil {
  424. return activePods, activeDeployments, fmt.Errorf("failed to create reid worker edgemesh service: %w", err)
  425. }
  426. reidServiceURL := fmt.Sprintf("%s:%d", reidServiceHost, reidServicePort)
  427. // create user worker deployment
  428. userWorkerReplicas := int32(1)
  429. userWorkerDeployment := &appsv1.DeploymentSpec{
  430. Replicas: &userWorkerReplicas,
  431. Template: service.Spec.UserWorker.Template,
  432. }
  433. var userWorkerParam runtime.WorkerParam
  434. userWorkerParam.WorkerType = objectSearchUserWorker
  435. userWorkerParam.Env = map[string]string{
  436. "NAMESPACE": service.Namespace,
  437. "SERVICE_NAME": service.Name,
  438. "WORKER_NAME": "userworker-" + utilrand.String(5),
  439. }
  440. _, err = runtime.CreateDeploymentWithTemplate(c.kubeClient, service, userWorkerDeployment, &userWorkerParam)
  441. if err != nil {
  442. return activePods, activeDeployments, fmt.Errorf("failed to create user worker: %w", err)
  443. }
  444. activeDeployments++
  445. // create user worker service
  446. userWorkerHost, err := runtime.CreateEdgeMeshService(c.kubeClient, service, objectSearchUserWorker, userWorkerPort)
  447. if err != nil {
  448. return activePods, activeDeployments, fmt.Errorf("failed to create edgemesh service: %w", err)
  449. }
  450. userWorkerURL := fmt.Sprintf("%s:%d", userWorkerHost, userWorkerPort)
  451. // create tracking worker pods
  452. var trackingWorkerParam runtime.WorkerParam
  453. trackingWorkerParam.WorkerType = objectSearchTrackingWorker
  454. for i, trackingWorker := range service.Spec.TrackingWorkers {
  455. trackingWorkerParam.Env = map[string]string{
  456. "NAMESPACE": service.Namespace,
  457. "SERVICE_NAME": service.Name,
  458. "WORKER_NAME": "trackingworker-" + utilrand.String(5),
  459. "USERWORKER_URL": userWorkerURL,
  460. "EDGEMESH_URL": reidServiceURL,
  461. }
  462. _, err = runtime.CreatePodWithTemplate(c.kubeClient, service, &trackingWorker.Template, &trackingWorkerParam)
  463. if err != nil {
  464. return activePods, activeDeployments, fmt.Errorf("failed to create %dth tracking worker: %w", i, err)
  465. }
  466. activePods++
  467. }
  468. return activePods, activeDeployments, err
  469. }
  470. // New creates a new ObjectSearchService controller that keeps the relevant pods
  471. // in sync with their corresponding ObjectSearchService objects.
  472. func New(cc *runtime.ControllerContext) (runtime.FeatureControllerI, error) {
  473. cfg := cc.Config
  474. podInformer := cc.KubeInformerFactory.Core().V1().Pods()
  475. deploymentInformer := cc.KubeInformerFactory.Apps().V1().Deployments()
  476. serviceInformer := cc.SednaInformerFactory.Sedna().V1alpha1().ObjectSearchServices()
  477. eventBroadcaster := record.NewBroadcaster()
  478. eventBroadcaster.StartRecordingToSink(&v1core.EventSinkImpl{Interface: cc.KubeClient.CoreV1().Events("")})
  479. jc := &Controller{
  480. kubeClient: cc.KubeClient,
  481. client: cc.SednaClient.SednaV1alpha1(),
  482. queue: workqueue.NewNamedRateLimitingQueue(workqueue.NewItemExponentialFailureRateLimiter(runtime.DefaultBackOff, runtime.MaxBackOff), "objectsearchservice"),
  483. recorder: eventBroadcaster.NewRecorder(scheme.Scheme, v1.EventSource{Component: "objectsearch-controller"}),
  484. cfg: cfg,
  485. }
  486. serviceInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
  487. AddFunc: func(obj interface{}) {
  488. jc.enqueueController(obj, true)
  489. },
  490. UpdateFunc: func(old, cur interface{}) {
  491. jc.enqueueController(cur, true)
  492. },
  493. DeleteFunc: func(obj interface{}) {
  494. jc.enqueueController(obj, true)
  495. },
  496. })
  497. jc.serviceLister = serviceInformer.Lister()
  498. jc.serviceStoreSynced = serviceInformer.Informer().HasSynced
  499. podInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
  500. AddFunc: jc.addPod,
  501. UpdateFunc: jc.updatePod,
  502. DeleteFunc: jc.deletePod,
  503. })
  504. jc.podStore = podInformer.Lister()
  505. jc.podStoreSynced = podInformer.Informer().HasSynced
  506. deploymentInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
  507. AddFunc: jc.addDeployment,
  508. UpdateFunc: jc.updateDeployment,
  509. DeleteFunc: jc.deleteDeployment,
  510. })
  511. jc.deploymentsLister = deploymentInformer.Lister()
  512. jc.deploymentsSynced = deploymentInformer.Informer().HasSynced
  513. return jc, nil
  514. }