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.

jointinferenceservice.go 22 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640
  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 jointinference
  14. import (
  15. "context"
  16. "encoding/json"
  17. "fmt"
  18. "reflect"
  19. "strconv"
  20. "strings"
  21. "time"
  22. appsv1 "k8s.io/api/apps/v1"
  23. v1 "k8s.io/api/core/v1"
  24. "k8s.io/apimachinery/pkg/api/errors"
  25. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  26. utilrand "k8s.io/apimachinery/pkg/util/rand"
  27. utilruntime "k8s.io/apimachinery/pkg/util/runtime"
  28. "k8s.io/apimachinery/pkg/util/wait"
  29. "k8s.io/apimachinery/pkg/watch"
  30. "k8s.io/client-go/kubernetes"
  31. "k8s.io/client-go/kubernetes/scheme"
  32. v1core "k8s.io/client-go/kubernetes/typed/core/v1"
  33. appslisters "k8s.io/client-go/listers/apps/v1"
  34. "k8s.io/client-go/tools/cache"
  35. "k8s.io/client-go/tools/record"
  36. "k8s.io/client-go/util/workqueue"
  37. "k8s.io/klog/v2"
  38. sednav1 "github.com/kubeedge/sedna/pkg/apis/sedna/v1alpha1"
  39. sednaclientset "github.com/kubeedge/sedna/pkg/client/clientset/versioned/typed/sedna/v1alpha1"
  40. sednav1listers "github.com/kubeedge/sedna/pkg/client/listers/sedna/v1alpha1"
  41. "github.com/kubeedge/sedna/pkg/globalmanager/config"
  42. "github.com/kubeedge/sedna/pkg/globalmanager/runtime"
  43. )
  44. const (
  45. // Name is this controller name
  46. Name = "JointInference"
  47. // KindName is the kind name of CR this controller controls
  48. KindName = "JointInferenceService"
  49. )
  50. const (
  51. jointInferenceForEdge = "Edge"
  52. jointInferenceForCloud = "Cloud"
  53. BigModelPort = 5000
  54. )
  55. // gvk contains the schema.GroupVersionKind for this controller type.
  56. var gvk = sednav1.SchemeGroupVersion.WithKind(KindName)
  57. // Controller ensures that all JointInferenceService objects
  58. // have corresponding deployments to run their configured workload.
  59. type Controller struct {
  60. kubeClient kubernetes.Interface
  61. client sednaclientset.SednaV1alpha1Interface
  62. // deploymentsSynced returns true if the deployment store has been synced at least once.
  63. deploymentsSynced cache.InformerSynced
  64. // A store of deployment
  65. deploymentsLister appslisters.DeploymentLister
  66. // serviceStoreSynced returns true if the JointInferenceService store has been synced at least once.
  67. serviceStoreSynced cache.InformerSynced
  68. // A store of service
  69. serviceLister sednav1listers.JointInferenceServiceLister
  70. // JointInferenceServices that need to be updated
  71. queue workqueue.RateLimitingInterface
  72. recorder record.EventRecorder
  73. cfg *config.ControllerConfig
  74. sendToEdgeFunc runtime.DownstreamSendFunc
  75. }
  76. // Run starts the main goroutine responsible for watching and syncing services.
  77. func (c *Controller) Run(stopCh <-chan struct{}) {
  78. workers := 1
  79. defer utilruntime.HandleCrash()
  80. defer c.queue.ShutDown()
  81. klog.Infof("Starting %s controller", Name)
  82. defer klog.Infof("Shutting down %s controller", Name)
  83. if !cache.WaitForNamedCacheSync(Name, stopCh, c.deploymentsSynced, c.serviceStoreSynced) {
  84. klog.Errorf("failed to wait for %s caches to sync", Name)
  85. return
  86. }
  87. klog.Infof("Starting %s workers", Name)
  88. for i := 0; i < workers; i++ {
  89. go wait.Until(c.worker, time.Second, stopCh)
  90. }
  91. <-stopCh
  92. }
  93. // obj could be an *sednav1.JointInferenceService, or a DeletionFinalStateUnknown marker item,
  94. // immediate tells the controller to update the status right away, and should
  95. // happen ONLY when there was a successful pod run.
  96. func (c *Controller) enqueueController(obj interface{}, immediate bool) {
  97. key, err := cache.DeletionHandlingMetaNamespaceKeyFunc(obj)
  98. if err != nil {
  99. klog.Warningf("Couldn't get key for object %+v: %v", obj, err)
  100. return
  101. }
  102. backoff := time.Duration(0)
  103. if !immediate {
  104. backoff = runtime.GetBackoff(c.queue, key)
  105. }
  106. c.queue.AddAfter(key, backoff)
  107. }
  108. // worker runs a worker thread that just dequeues items, processes them, and marks them done.
  109. // It enforces that the sync is never invoked concurrently with the same key.
  110. func (c *Controller) worker() {
  111. for c.processNextWorkItem() {
  112. }
  113. }
  114. func (c *Controller) processNextWorkItem() bool {
  115. key, quit := c.queue.Get()
  116. if quit {
  117. return false
  118. }
  119. defer c.queue.Done(key)
  120. forget, err := c.sync(key.(string))
  121. if err == nil {
  122. if forget {
  123. c.queue.Forget(key)
  124. }
  125. return true
  126. }
  127. klog.Warningf("Error syncing jointinference service: %v", err)
  128. c.queue.AddRateLimited(key)
  129. return true
  130. }
  131. // sync will sync the jointinferenceservice with the given key.
  132. // This function is not meant to be invoked concurrently with the same key.
  133. func (c *Controller) sync(key string) (bool, error) {
  134. startTime := time.Now()
  135. defer func() {
  136. klog.V(4).Infof("Finished syncing jointinference service %q (%v)", key, time.Since(startTime))
  137. }()
  138. ns, name, err := cache.SplitMetaNamespaceKey(key)
  139. if err != nil {
  140. return false, err
  141. }
  142. if len(ns) == 0 || len(name) == 0 {
  143. return false, fmt.Errorf("invalid jointinference service key %q: either namespace or name is missing", key)
  144. }
  145. // Use Lister to obtain the JointInferenceService object (Lister is a cache reading mechanism).
  146. // If the service does not exist (has been deleted), log the message and return true, indicating that this object no longer needs to be synchronized.
  147. // If the acquisition fails but not because the object has been deleted, return an error.
  148. sharedService, err := c.serviceLister.JointInferenceServices(ns).Get(name)
  149. if err != nil {
  150. if errors.IsNotFound(err) {
  151. klog.V(4).Infof("JointInferenceService has been deleted: %v", key)
  152. return true, nil
  153. }
  154. return false, err
  155. }
  156. service := *sharedService
  157. // if service was finished previously, we don't want to redo the termination
  158. if isServiceFinished(&service) {
  159. return true, nil
  160. }
  161. // set kind for service in case that the kind is None
  162. // more details at https://github.com/kubernetes/kubernetes/issues/3030
  163. service.SetGroupVersionKind(gvk)
  164. selectorDeployments, _ := runtime.GenerateSelector(&service)
  165. deployments, err := c.deploymentsLister.Deployments(service.Namespace).List(selectorDeployments)
  166. if err != nil {
  167. return false, err
  168. }
  169. klog.V(4).Infof("list jointinference service %v/%v, %v deployments: %v", service.Namespace, service.Name, len(deployments), deployments)
  170. latestConditionLen := len(service.Status.Conditions)
  171. activeDeployments := runtime.CalcActiveDeploymentCount(deployments)
  172. var failed int32 = 0
  173. // neededCounts means that two deployments should be created successfully in a jointinference service currently
  174. // two deployments consist of edge deployment and cloud deployment
  175. var neededCounts int32 = 2
  176. if service.Status.StartTime == nil {
  177. now := metav1.Now()
  178. service.Status.StartTime = &now
  179. } else {
  180. failed = neededCounts - activeDeployments
  181. }
  182. var manageServiceErr error
  183. serviceFailed := false
  184. var latestConditionType sednav1.JointInferenceServiceConditionType = ""
  185. // get the latest condition type
  186. // based on that condition updated is appended, not inserted.
  187. jobConditions := service.Status.Conditions
  188. if len(jobConditions) > 0 {
  189. latestConditionType = (jobConditions)[len(jobConditions)-1].Type
  190. }
  191. var newCondtionType sednav1.JointInferenceServiceConditionType
  192. var reason string
  193. var message string
  194. if failed > 0 {
  195. serviceFailed = true
  196. // TODO: get the failed worker, and knows that which worker fails, edge inference worker or cloud inference worker
  197. reason = "workerFailed"
  198. message = "the worker of service failed"
  199. newCondtionType = sednav1.JointInferenceServiceCondFailed
  200. c.recorder.Event(&service, v1.EventTypeWarning, reason, message)
  201. } else {
  202. if len(deployments) == 0 {
  203. activeDeployments, manageServiceErr = c.createWorkers(&service)
  204. }
  205. if manageServiceErr != nil {
  206. serviceFailed = true
  207. message = error.Error(manageServiceErr)
  208. newCondtionType = sednav1.JointInferenceServiceCondFailed
  209. failed = neededCounts - activeDeployments
  210. } else {
  211. // TODO: handle the case that the pod phase is PodSucceeded
  212. newCondtionType = sednav1.JointInferenceServiceCondRunning
  213. }
  214. }
  215. //
  216. if newCondtionType != latestConditionType {
  217. service.Status.Conditions = append(service.Status.Conditions, newServiceCondition(newCondtionType, reason, message))
  218. }
  219. forget := false
  220. // no need to update the jointinferenceservice if the status hasn't changed since last time
  221. if service.Status.Active != activeDeployments || service.Status.Failed != failed || len(service.Status.Conditions) != latestConditionLen {
  222. service.Status.Active = activeDeployments
  223. service.Status.Failed = failed
  224. if err := c.updateStatus(&service); err != nil {
  225. return forget, err
  226. }
  227. if serviceFailed && !isServiceFinished(&service) {
  228. // returning an error will re-enqueue jointinferenceservice after the backoff period
  229. return forget, fmt.Errorf("failed deployment(s) detected for jointinference service key %q", key)
  230. }
  231. forget = true
  232. }
  233. return forget, manageServiceErr
  234. }
  235. // newServiceCondition creates a new joint condition
  236. func newServiceCondition(conditionType sednav1.JointInferenceServiceConditionType, reason, message string) sednav1.JointInferenceServiceCondition {
  237. return sednav1.JointInferenceServiceCondition{
  238. Type: conditionType,
  239. Status: v1.ConditionTrue,
  240. LastHeartbeatTime: metav1.Now(),
  241. LastTransitionTime: metav1.Now(),
  242. Reason: reason,
  243. Message: message,
  244. }
  245. }
  246. func (c *Controller) updateStatus(service *sednav1.JointInferenceService) error {
  247. client := c.client.JointInferenceServices(service.Namespace)
  248. return runtime.RetryUpdateStatus(service.Name, service.Namespace, func() error {
  249. newService, err := client.Get(context.TODO(), service.Name, metav1.GetOptions{})
  250. if err != nil {
  251. return err
  252. }
  253. newService.Status = service.Status
  254. _, err = client.UpdateStatus(context.TODO(), newService, metav1.UpdateOptions{})
  255. return err
  256. })
  257. }
  258. func isServiceFinished(j *sednav1.JointInferenceService) bool {
  259. for _, c := range j.Status.Conditions {
  260. if (c.Type == sednav1.JointInferenceServiceCondFailed) && c.Status == v1.ConditionTrue {
  261. return true
  262. }
  263. }
  264. return false
  265. }
  266. func (c *Controller) createWorkers(service *sednav1.JointInferenceService) (active int32, err error) {
  267. active = 0
  268. var bigModelPort int32 = BigModelPort
  269. // create cloud worker
  270. err = c.createCloudWorker(service, bigModelPort)
  271. if err != nil {
  272. return active, err
  273. }
  274. active++
  275. // create k8s service for cloud deployment
  276. bigModelHost, err := runtime.CreateEdgeMeshService(c.kubeClient, service, jointInferenceForCloud, bigModelPort)
  277. if err != nil {
  278. return active, err
  279. }
  280. // create edge worker
  281. err = c.createEdgeWorker(service, bigModelHost, bigModelPort)
  282. if err != nil {
  283. return active, err
  284. }
  285. active++
  286. return active, err
  287. }
  288. // enqueueByDeployment enqueues the JointInferenceService object of the specified deployment.
  289. func (c *Controller) enqueueByDeployment(deployment *appsv1.Deployment, immediate bool) {
  290. controllerRef := metav1.GetControllerOf(deployment)
  291. klog.Infof("Deployment enqueued %v", deployment.Kind)
  292. if controllerRef == nil {
  293. return
  294. }
  295. if controllerRef.Kind != gvk.Kind {
  296. return
  297. }
  298. service, err := c.serviceLister.JointInferenceServices(deployment.Namespace).Get(controllerRef.Name)
  299. if err != nil {
  300. return
  301. }
  302. if service.UID != controllerRef.UID {
  303. return
  304. }
  305. c.enqueueController(service, immediate)
  306. }
  307. // When a deployment is created, enqueue the controller that manages it and update it's expectations.
  308. func (c *Controller) addDeployment(obj interface{}) {
  309. deployment := obj.(*appsv1.Deployment)
  310. c.enqueueByDeployment(deployment, true)
  311. }
  312. // When a deployment is updated, figure out what jointinferenceservice manage it and wake them up.
  313. func (c *Controller) updateDeployment(old, cur interface{}) {
  314. oldD := old.(*appsv1.Deployment)
  315. curD := cur.(*appsv1.Deployment)
  316. // no deployment update, no queue
  317. if curD.ResourceVersion == oldD.ResourceVersion {
  318. return
  319. }
  320. c.addDeployment(curD)
  321. }
  322. // deleteDeployment enqueues the jointinferenceservice obj When a deleteDeployment is deleted
  323. func (c *Controller) deleteDeployment(obj interface{}) {
  324. deployment, ok := obj.(*appsv1.Deployment)
  325. if !ok {
  326. tombstone, ok := obj.(cache.DeletedFinalStateUnknown)
  327. if !ok {
  328. klog.Warningf("couldn't get object from tombstone %+v", obj)
  329. return
  330. }
  331. deployment, ok = tombstone.Obj.(*appsv1.Deployment)
  332. if !ok {
  333. klog.Warningf("tombstone contained object that is not a Deployment %+v", obj)
  334. return
  335. }
  336. }
  337. // If the deployment is accidentally deleted, recreate the deployment.
  338. newDeployment := deployment.DeepCopy()
  339. serviceName := func(input string) string {
  340. return strings.Split(input, "-deployment")[0]
  341. }(newDeployment.Name)
  342. _, err := c.serviceLister.JointInferenceServices(newDeployment.Namespace).Get(serviceName)
  343. if !errors.IsNotFound(err) {
  344. // Remove unnecessary metadata.
  345. newDeployment.ResourceVersion = ""
  346. newDeployment.UID = ""
  347. // Create a new deployment.
  348. _, err := c.kubeClient.AppsV1().Deployments(newDeployment.Namespace).Create(context.TODO(), newDeployment, metav1.CreateOptions{})
  349. if err != nil {
  350. klog.Errorf("failed to recreate deployment %s: %v", deployment.Name, err)
  351. return
  352. }
  353. }
  354. klog.Infof("Successfully recreated deployment %s", deployment.Name)
  355. c.enqueueByDeployment(newDeployment, true)
  356. }
  357. func (c *Controller) updateInferenceServices(old, cur interface{}) error {
  358. oldService := old.(*sednav1.JointInferenceService)
  359. newService := cur.(*sednav1.JointInferenceService)
  360. // Check if the cloud worker configuration has changed
  361. if !reflect.DeepEqual(oldService.Spec.CloudWorker, newService.Spec.CloudWorker) {
  362. // Update cloud worker and log any errors
  363. if err := c.updateCloudWorker(newService); err != nil {
  364. klog.Errorf("Failed to update cloud worker for service %s/%s: %v", newService.Namespace, newService.Name, err)
  365. }
  366. }
  367. // Retrieve the address of the cloud inference service
  368. var bigModelHost string
  369. svc, err := c.kubeClient.CoreV1().Services(oldService.Namespace).Get(context.Background(),
  370. strings.ToLower(oldService.Name+"-"+jointInferenceForCloud), metav1.GetOptions{})
  371. if err != nil {
  372. if errors.IsNotFound(err) {
  373. // If the service does not exist, create a new one and retrieve its address
  374. klog.Info("Cloud service not found, creating new service...")
  375. bigModelHost, err = runtime.CreateEdgeMeshService(c.kubeClient, oldService, jointInferenceForCloud, BigModelPort)
  376. if err != nil {
  377. klog.Errorf("Failed to create EdgeMesh service for service %s/%s: %v", oldService.Namespace, oldService.Name, err)
  378. }
  379. } else {
  380. klog.Errorf("Failed to get cloud service %s/%s: %v", oldService.Namespace, oldService.Name, err)
  381. }
  382. } else {
  383. bigModelHost = fmt.Sprintf("%s.%s", svc.Name, svc.Namespace)
  384. }
  385. // Check if the edge worker configuration has changed
  386. if !reflect.DeepEqual(oldService.Spec.EdgeWorker, newService.Spec.EdgeWorker) {
  387. // Update edge worker and log any errors
  388. if err := c.updateEdgeWorker(newService, bigModelHost); err != nil {
  389. klog.Errorf("Failed to update edge worker for service %s/%s: %v", newService.Namespace, newService.Name, err)
  390. }
  391. }
  392. return nil
  393. }
  394. func (c *Controller) createOrUpdateWorker(service *sednav1.JointInferenceService, workerType string, bigModelHost string, bigModelPort int32, create bool) error {
  395. var modelName string
  396. var modelTemplate v1.PodTemplateSpec
  397. var hpa *sednav1.HPA
  398. var workerParam runtime.WorkerParam
  399. deploymentName := service.GetName() + "-" + "deployment" + "-" + strings.ToLower(workerType)
  400. // Set the corresponding parameters according to the workerType.
  401. switch workerType {
  402. case jointInferenceForCloud:
  403. modelName = service.Spec.CloudWorker.Model.Name
  404. modelTemplate = *service.Spec.CloudWorker.Template.DeepCopy()
  405. hpa = service.Spec.CloudWorker.HPA.DeepCopy()
  406. workerParam.Env = map[string]string{
  407. "BIG_MODEL_BIND_PORT": strconv.Itoa(int(bigModelPort)),
  408. }
  409. workerParam.WorkerType = workerType
  410. case jointInferenceForEdge:
  411. modelName = service.Spec.EdgeWorker.Model.Name
  412. modelTemplate = *service.Spec.EdgeWorker.Template.DeepCopy()
  413. hpa = service.Spec.EdgeWorker.HPA.DeepCopy()
  414. HEMParameterJSON, _ := json.Marshal(service.Spec.EdgeWorker.HardExampleMining.Parameters)
  415. HEMParameterString := string(HEMParameterJSON)
  416. workerParam.Env = map[string]string{
  417. "BIG_MODEL_IP": bigModelHost,
  418. "BIG_MODEL_PORT": strconv.Itoa(int(bigModelPort)),
  419. "HEM_NAME": service.Spec.EdgeWorker.HardExampleMining.Name,
  420. "HEM_PARAMETERS": HEMParameterString,
  421. "LC_SERVER": c.cfg.LC.Server,
  422. }
  423. workerParam.WorkerType = workerType
  424. }
  425. // get the model.
  426. model, err := c.client.Models(service.Namespace).Get(context.Background(), modelName, metav1.GetOptions{})
  427. if err != nil {
  428. return fmt.Errorf("failed to get model %s: %w", modelName, err)
  429. }
  430. secretName := model.Spec.CredentialName
  431. var modelSecret *v1.Secret
  432. if secretName != "" {
  433. modelSecret, _ = c.kubeClient.CoreV1().Secrets(service.Namespace).Get(context.TODO(), secretName, metav1.GetOptions{})
  434. }
  435. // Fill in the mounting configuration of workerParam.
  436. workerParam.Mounts = append(workerParam.Mounts, runtime.WorkerMount{
  437. URL: &runtime.MountURL{
  438. URL: model.Spec.URL,
  439. Secret: modelSecret,
  440. DownloadByInitializer: true,
  441. },
  442. Name: "model",
  443. EnvName: "MODEL_URL",
  444. })
  445. // Set other common environment variables.
  446. workerParam.Env["NAMESPACE"] = service.Namespace
  447. workerParam.Env["SERVICE_NAME"] = service.Name
  448. workerParam.Env["WORKER_NAME"] = strings.ToLower(workerType) + "worker-" + utilrand.String(5)
  449. // Set the group version kind.
  450. service.SetGroupVersionKind(gvk)
  451. // Create or update Deployment.
  452. if create {
  453. _, err = runtime.CreateDeploymentWithTemplate(c.kubeClient, service, &appsv1.DeploymentSpec{Template: modelTemplate}, &workerParam)
  454. // create HPA
  455. if hpa != nil {
  456. return runtime.CreateHPA(c.kubeClient, service, "Deployment", deploymentName, workerType, hpa)
  457. }
  458. } else {
  459. workerName := service.Name + "-deployment-" + strings.ToLower(workerType)
  460. existingDeployment, err := c.deploymentsLister.Deployments(service.Namespace).Get(workerName)
  461. if err != nil {
  462. return fmt.Errorf("get %s Deployment failed: %v", strings.ToLower(workerType), err)
  463. }
  464. newDeployment := existingDeployment.DeepCopy()
  465. newDeployment.Spec.Template = modelTemplate
  466. _, err = runtime.UpdateDeploymentWithTemplate(c.kubeClient, service, newDeployment, &workerParam)
  467. // update HPA
  468. if hpa != nil {
  469. return runtime.UpdateHPA(c.kubeClient, service, "Deployment", deploymentName, workerType, hpa)
  470. }
  471. return runtime.DeleteHPA(c.kubeClient, service.GetNamespace(), "hpa-"+deploymentName)
  472. }
  473. return err
  474. }
  475. func (c *Controller) createCloudWorker(service *sednav1.JointInferenceService, bigModelPort int32) error {
  476. return c.createOrUpdateWorker(service, jointInferenceForCloud, "", bigModelPort, true)
  477. }
  478. func (c *Controller) createEdgeWorker(service *sednav1.JointInferenceService, bigModelHost string, bigModelPort int32) error {
  479. return c.createOrUpdateWorker(service, jointInferenceForEdge, bigModelHost, bigModelPort, true)
  480. }
  481. func (c *Controller) updateCloudWorker(newservice *sednav1.JointInferenceService) error {
  482. return c.createOrUpdateWorker(newservice, jointInferenceForCloud, "", BigModelPort, false)
  483. }
  484. func (c *Controller) updateEdgeWorker(newservice *sednav1.JointInferenceService, bigModelHost string) error {
  485. return c.createOrUpdateWorker(newservice, jointInferenceForEdge, bigModelHost, BigModelPort, false)
  486. }
  487. // New creates a new JointInferenceService controller that keeps the relevant deployments
  488. // in sync with their corresponding JointInferenceService objects.
  489. func New(cc *runtime.ControllerContext) (runtime.FeatureControllerI, error) {
  490. cfg := cc.Config
  491. deploymentInformer := cc.KubeInformerFactory.Apps().V1().Deployments()
  492. serviceInformer := cc.SednaInformerFactory.Sedna().V1alpha1().JointInferenceServices()
  493. eventBroadcaster := record.NewBroadcaster()
  494. eventBroadcaster.StartRecordingToSink(&v1core.EventSinkImpl{Interface: cc.KubeClient.CoreV1().Events("")})
  495. jc := &Controller{
  496. kubeClient: cc.KubeClient,
  497. client: cc.SednaClient.SednaV1alpha1(),
  498. queue: workqueue.NewNamedRateLimitingQueue(workqueue.NewItemExponentialFailureRateLimiter(runtime.DefaultBackOff, runtime.MaxBackOff), "jointinferenceservice"),
  499. recorder: eventBroadcaster.NewRecorder(scheme.Scheme, v1.EventSource{Component: "jointinferenceservice-controller"}),
  500. cfg: cfg,
  501. }
  502. serviceInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
  503. AddFunc: func(obj interface{}) {
  504. jc.enqueueController(obj, true)
  505. jc.syncToEdge(watch.Added, obj)
  506. },
  507. UpdateFunc: func(old, cur interface{}) {
  508. jc.enqueueController(cur, true)
  509. jc.updateInferenceServices(old, cur)
  510. jc.syncToEdge(watch.Modified, cur)
  511. },
  512. DeleteFunc: func(obj interface{}) {
  513. jc.enqueueController(obj, true)
  514. jc.syncToEdge(watch.Deleted, obj)
  515. },
  516. })
  517. jc.serviceLister = serviceInformer.Lister()
  518. jc.serviceStoreSynced = serviceInformer.Informer().HasSynced
  519. deploymentInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
  520. AddFunc: jc.addDeployment,
  521. UpdateFunc: jc.updateDeployment,
  522. DeleteFunc: jc.deleteDeployment,
  523. })
  524. jc.deploymentsLister = deploymentInformer.Lister()
  525. jc.deploymentsSynced = deploymentInformer.Informer().HasSynced
  526. return jc, nil
  527. }