github.com/jingruilea/kubeedge@v1.2.0-beta.0.0.20200410162146-4bb8902b3879/edge/pkg/metamanager/process.go (about) 1 package metamanager 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "strings" 7 "time" 8 9 "k8s.io/api/core/v1" 10 "k8s.io/klog" 11 12 "github.com/kubeedge/beehive/pkg/common/util" 13 beehiveContext "github.com/kubeedge/beehive/pkg/core/context" 14 "github.com/kubeedge/beehive/pkg/core/model" 15 "github.com/kubeedge/kubeedge/common/constants" 16 connect "github.com/kubeedge/kubeedge/edge/pkg/common/cloudconnection" 17 messagepkg "github.com/kubeedge/kubeedge/edge/pkg/common/message" 18 "github.com/kubeedge/kubeedge/edge/pkg/common/modules" 19 metaManagerConfig "github.com/kubeedge/kubeedge/edge/pkg/metamanager/config" 20 "github.com/kubeedge/kubeedge/edge/pkg/metamanager/dao" 21 ) 22 23 //Constants to check metamanager processes 24 const ( 25 OK = "OK" 26 27 GroupResource = "resource" 28 OperationMetaSync = "meta-internal-sync" 29 30 OperationFunctionAction = "action" 31 32 OperationFunctionActionResult = "action_result" 33 34 EdgeFunctionModel = "edgefunction" 35 CloudFunctionModel = "funcmgr" 36 CloudControlerModel = "edgecontroller" 37 ) 38 39 func feedbackError(err error, info string, request model.Message) { 40 errInfo := "Something wrong" 41 if err != nil { 42 errInfo = fmt.Sprintf(info+": %v", err) 43 } 44 errResponse := model.NewErrorMessage(&request, errInfo).SetRoute(MetaManagerModuleName, request.GetGroup()) 45 if request.GetSource() == modules.EdgedModuleName { 46 sendToEdged(errResponse, request.IsSync()) 47 } else { 48 sendToCloud(errResponse) 49 } 50 } 51 52 func sendToEdged(message *model.Message, sync bool) { 53 if sync { 54 beehiveContext.SendResp(*message) 55 } else { 56 beehiveContext.Send(modules.EdgedModuleName, *message) 57 } 58 } 59 60 func sendToEdgeMesh(message *model.Message, sync bool) { 61 if sync { 62 beehiveContext.SendResp(*message) 63 } else { 64 beehiveContext.Send(modules.EdgeMeshModuleName, *message) 65 } 66 } 67 68 func sendToCloud(message *model.Message) { 69 beehiveContext.SendToGroup(string(metaManagerConfig.Config.ContextSendGroup), *message) 70 } 71 72 // Resource format: <namespace>/<restype>[/resid] 73 // return <reskey, restype, resid> 74 func parseResource(resource string) (string, string, string) { 75 tokens := strings.Split(resource, constants.ResourceSep) 76 resType := "" 77 resID := "" 78 switch len(tokens) { 79 case 2: 80 resType = tokens[len(tokens)-1] 81 case 3: 82 resType = tokens[len(tokens)-2] 83 resID = tokens[len(tokens)-1] 84 default: 85 } 86 return resource, resType, resID 87 } 88 89 // is resource type require remote query 90 func requireRemoteQuery(resType string) bool { 91 return resType == model.ResourceTypeConfigmap || 92 resType == model.ResourceTypeSecret || 93 resType == constants.ResourceTypeEndpoints || 94 resType == constants.ResourceTypePersistentVolume || 95 resType == constants.ResourceTypePersistentVolumeClaim || 96 resType == constants.ResourceTypeVolumeAttachment || 97 resType == model.ResourceTypeNode 98 } 99 100 // if resource type is EdgeMesh related 101 func isEdgeMeshResource(resType string) bool { 102 return resType == constants.ResourceTypeService || 103 resType == constants.ResourceTypeServiceList || 104 resType == constants.ResourceTypeEndpoints || 105 resType == model.ResourceTypePodlist 106 } 107 108 func isConnected() bool { 109 return metaManagerConfig.Connected 110 } 111 112 func msgDebugInfo(message *model.Message) string { 113 return fmt.Sprintf("msgID[%s] resource[%s]", message.GetID(), message.GetResource()) 114 } 115 116 func resourceUnchanged(resType string, resKey string, content []byte) bool { 117 if resType == model.ResourceTypePodStatus { 118 dbRecord, err := dao.QueryMeta("key", resKey) 119 if err == nil && len(*dbRecord) > 0 && string(content) == (*dbRecord)[0] { 120 return true 121 } 122 } 123 124 return false 125 } 126 127 func (m *metaManager) processInsert(message model.Message) { 128 var err error 129 var content []byte 130 switch message.GetContent().(type) { 131 case []uint8: 132 content = message.GetContent().([]byte) 133 default: 134 content, err = json.Marshal(message.GetContent()) 135 if err != nil { 136 klog.Errorf("marshal update message content failed, %s", msgDebugInfo(&message)) 137 feedbackError(err, "Error to marshal message content", message) 138 return 139 } 140 } 141 resKey, resType, _ := parseResource(message.GetResource()) 142 switch resType { 143 case constants.ResourceTypeServiceList: 144 var svcList []v1.Service 145 err = json.Unmarshal(content, &svcList) 146 if err != nil { 147 klog.Errorf("Unmarshal insert message content failed, %s", msgDebugInfo(&message)) 148 feedbackError(err, "Error to unmarshal", message) 149 return 150 } 151 for _, svc := range svcList { 152 data, err := json.Marshal(svc) 153 if err != nil { 154 klog.Errorf("Marshal service content failed, %v", svc) 155 continue 156 } 157 meta := &dao.Meta{ 158 Key: fmt.Sprintf("%s/%s/%s", svc.Namespace, constants.ResourceTypeService, svc.Name), 159 Type: constants.ResourceTypeService, 160 Value: string(data)} 161 err = dao.SaveMeta(meta) 162 if err != nil { 163 klog.Errorf("Save meta %s failed, svc: %v, err: %v", string(data), svc, err) 164 feedbackError(err, "Error to save meta to DB", message) 165 return 166 } 167 } 168 default: 169 meta := &dao.Meta{ 170 Key: resKey, 171 Type: resType, 172 Value: string(content)} 173 err = dao.SaveMeta(meta) 174 if err != nil { 175 klog.Errorf("save meta failed, %s: %v", msgDebugInfo(&message), err) 176 feedbackError(err, "Error to save meta to DB", message) 177 return 178 } 179 } 180 181 if resType == constants.ResourceTypeListener { 182 // Notify edgemesh only 183 resp := message.NewRespByMessage(&message, nil) 184 sendToEdgeMesh(resp, true) 185 return 186 } 187 188 if isEdgeMeshResource(resType) { 189 // Notify edgemesh 190 sendToEdgeMesh(&message, false) 191 } else { 192 // Notify edged 193 sendToEdged(&message, false) 194 } 195 196 resp := message.NewRespByMessage(&message, OK) 197 sendToCloud(resp) 198 } 199 200 func (m *metaManager) processUpdate(message model.Message) { 201 var err error 202 var content []byte 203 switch message.GetContent().(type) { 204 case []uint8: 205 content = message.GetContent().([]byte) 206 default: 207 content, err = json.Marshal(message.GetContent()) 208 if err != nil { 209 klog.Errorf("marshal update message content failed, %s", msgDebugInfo(&message)) 210 feedbackError(err, "Error to marshal message content", message) 211 return 212 } 213 } 214 215 resKey, resType, _ := parseResource(message.GetResource()) 216 if resType == constants.ResourceTypeServiceList || resType == constants.ResourceTypeEndpointsList || resType == model.ResourceTypePodlist { 217 switch resType { 218 case constants.ResourceTypeEndpointsList: 219 var epsList []v1.Endpoints 220 err = json.Unmarshal(content, &epsList) 221 if err != nil { 222 klog.Errorf("Unmarshal update message content failed, %s", msgDebugInfo(&message)) 223 feedbackError(err, "Error to unmarshal", message) 224 return 225 } 226 for _, eps := range epsList { 227 data, err := json.Marshal(eps) 228 if err != nil { 229 klog.Errorf("Marshal endpoints content failed, %v", eps) 230 continue 231 } 232 233 meta := &dao.Meta{ 234 Key: fmt.Sprintf("%s/%s/%s", eps.Namespace, constants.ResourceTypeEndpoints, eps.Name), 235 Type: constants.ResourceTypeEndpoints, 236 Value: string(data)} 237 err = dao.InsertOrUpdate(meta) 238 if err != nil { 239 klog.Errorf("Update meta failed, %v", eps) 240 continue 241 } 242 } 243 sendToEdgeMesh(&message, false) 244 resp := message.NewRespByMessage(&message, OK) 245 sendToCloud(resp) 246 return 247 case constants.ResourceTypeServiceList: 248 var svcList []v1.Service 249 err = json.Unmarshal(content, &svcList) 250 if err != nil { 251 klog.Errorf("Unmarshal update message content failed, %s", msgDebugInfo(&message)) 252 feedbackError(err, "Error to unmarshal", message) 253 return 254 } 255 for _, svc := range svcList { 256 data, err := json.Marshal(svc) 257 if err != nil { 258 klog.Errorf("Marshal service content failed, %v", svc) 259 continue 260 } 261 262 meta := &dao.Meta{ 263 Key: fmt.Sprintf("%s/%s/%s", svc.Namespace, constants.ResourceTypeService, svc.Name), 264 Type: constants.ResourceTypeService, 265 Value: string(data)} 266 err = dao.InsertOrUpdate(meta) 267 if err != nil { 268 klog.Errorf("Update meta failed, %v", svc) 269 continue 270 } 271 } 272 sendToEdgeMesh(&message, false) 273 resp := message.NewRespByMessage(&message, OK) 274 sendToCloud(resp) 275 return 276 case model.ResourceTypePodlist: 277 meta := &dao.Meta{ 278 Key: resKey, 279 Type: resType, 280 Value: string(content)} 281 err = dao.InsertOrUpdate(meta) 282 if err != nil { 283 klog.Errorf("Update meta failed, %s", msgDebugInfo(&message)) 284 feedbackError(err, "Error to update meta to DB", message) 285 return 286 } 287 sendToEdgeMesh(&message, false) 288 resp := message.NewRespByMessage(&message, OK) 289 sendToCloud(resp) 290 return 291 default: 292 klog.Warningf("Resource type %s unknown", resType) 293 return 294 } 295 } 296 297 if resourceUnchanged(resType, resKey, content) { 298 resp := message.NewRespByMessage(&message, OK) 299 sendToEdged(resp, message.IsSync()) 300 klog.Infof("resource[%s] unchanged, no notice", resKey) 301 return 302 } 303 304 meta := &dao.Meta{ 305 Key: resKey, 306 Type: resType, 307 Value: string(content)} 308 err = dao.InsertOrUpdate(meta) 309 if err != nil { 310 klog.Errorf("update meta failed, %s", msgDebugInfo(&message)) 311 feedbackError(err, "Error to update meta to DB", message) 312 return 313 } 314 315 msgSource := message.GetSource() 316 switch msgSource { 317 //case core.EdgedModuleName: 318 case modules.EdgedModuleName: 319 sendToCloud(&message) 320 resp := message.NewRespByMessage(&message, OK) 321 sendToEdged(resp, message.IsSync()) 322 case CloudControlerModel: 323 if isEdgeMeshResource(resType) { 324 sendToEdgeMesh(&message, message.IsSync()) 325 } else { 326 sendToEdged(&message, message.IsSync()) 327 } 328 resp := message.NewRespByMessage(&message, OK) 329 sendToCloud(resp) 330 case CloudFunctionModel: 331 beehiveContext.Send(EdgeFunctionModel, message) 332 case EdgeFunctionModel: 333 sendToCloud(&message) 334 default: 335 klog.Errorf("unsupport message source, %s", msgSource) 336 } 337 } 338 339 func (m *metaManager) processResponse(message model.Message) { 340 var err error 341 var content []byte 342 switch message.GetContent().(type) { 343 case []uint8: 344 content = message.GetContent().([]byte) 345 default: 346 content, err = json.Marshal(message.GetContent()) 347 if err != nil { 348 klog.Errorf("marshal response message content failed, %s", msgDebugInfo(&message)) 349 feedbackError(err, "Error to marshal message content", message) 350 return 351 } 352 } 353 354 resKey, resType, _ := parseResource(message.GetResource()) 355 meta := &dao.Meta{ 356 Key: resKey, 357 Type: resType, 358 Value: string(content)} 359 err = dao.InsertOrUpdate(meta) 360 if err != nil { 361 klog.Errorf("update meta failed, %s", msgDebugInfo(&message)) 362 feedbackError(err, "Error to update meta to DB", message) 363 return 364 } 365 366 // Notify edged or edgemesh if the data is coming from cloud 367 if message.GetSource() == CloudControlerModel { 368 if resType == constants.ResourceTypeService || resType == constants.ResourceTypeEndpoints { 369 sendToEdgeMesh(&message, message.IsSync()) 370 } else { 371 sendToEdged(&message, message.IsSync()) 372 } 373 } else { 374 // Send to cloud if the update request is coming from edged 375 sendToCloud(&message) 376 } 377 } 378 379 func (m *metaManager) processDelete(message model.Message) { 380 err := dao.DeleteMetaByKey(message.GetResource()) 381 if err != nil { 382 klog.Errorf("delete meta failed, %s", msgDebugInfo(&message)) 383 feedbackError(err, "Error to delete meta to DB", message) 384 return 385 } 386 387 _, resType, _ := parseResource(message.GetResource()) 388 if resType == constants.ResourceTypeService || resType == constants.ResourceTypeEndpoints { 389 // Notify edgemesh 390 sendToEdgeMesh(&message, false) 391 resp := message.NewRespByMessage(&message, OK) 392 sendToCloud(resp) 393 return 394 } 395 if resType == constants.ResourceTypeListener { 396 // Notify edgemesh only 397 resp := message.NewRespByMessage(&message, OK) 398 sendToEdgeMesh(resp, true) 399 return 400 } 401 // Notify edged 402 sendToEdged(&message, false) 403 resp := message.NewRespByMessage(&message, OK) 404 sendToCloud(resp) 405 } 406 407 func (m *metaManager) processQuery(message model.Message) { 408 resKey, resType, resID := parseResource(message.GetResource()) 409 var metas *[]string 410 var err error 411 if requireRemoteQuery(resType) && isConnected() { 412 metas, err = dao.QueryMeta("key", resKey) 413 if err != nil || len(*metas) == 0 || resType == model.ResourceTypeNode || resType == constants.ResourceTypeVolumeAttachment { 414 m.processRemoteQuery(message) 415 } else { 416 resp := message.NewRespByMessage(&message, *metas) 417 resp.SetRoute(MetaManagerModuleName, resp.GetGroup()) 418 sendToEdged(resp, message.IsSync()) 419 } 420 return 421 } 422 423 if resID == "" { 424 // Get specific type resources 425 metas, err = dao.QueryMeta("type", resType) 426 } else { 427 metas, err = dao.QueryMeta("key", resKey) 428 } 429 if err != nil { 430 klog.Errorf("query meta failed, %s", msgDebugInfo(&message)) 431 feedbackError(err, "Error to query meta in DB", message) 432 } else { 433 resp := message.NewRespByMessage(&message, *metas) 434 resp.SetRoute(MetaManagerModuleName, resp.GetGroup()) 435 if resType == constants.ResourceTypeService || resType == constants.ResourceTypeEndpoints || resType == constants.ResourceTypeListener { 436 sendToEdgeMesh(resp, message.IsSync()) 437 } else { 438 sendToEdged(resp, message.IsSync()) 439 } 440 } 441 } 442 443 func (m *metaManager) processRemoteQuery(message model.Message) { 444 go func() { 445 // TODO: retry 446 originalID := message.GetID() 447 message.UpdateID() 448 resp, err := beehiveContext.SendSync( 449 string(metaManagerConfig.Config.ContextSendModule), 450 message, 451 60*time.Second) // TODO: configurable 452 klog.Infof("########## process get: req[%+v], resp[%+v], err[%+v]", message, resp, err) 453 if err != nil { 454 klog.Errorf("remote query failed: %v", err) 455 feedbackError(err, "Error to query meta in DB", message) 456 return 457 } 458 459 var content []byte 460 switch resp.GetContent().(type) { 461 case []uint8: 462 content = resp.GetContent().([]byte) 463 default: 464 content, err = json.Marshal(resp.GetContent()) 465 if err != nil { 466 klog.Errorf("marshal remote query response content failed, %s", msgDebugInfo(&resp)) 467 feedbackError(err, "Error to marshal message content", message) 468 return 469 } 470 } 471 472 resKey, resType, _ := parseResource(message.GetResource()) 473 meta := &dao.Meta{ 474 Key: resKey, 475 Type: resType, 476 Value: string(content)} 477 err = dao.InsertOrUpdate(meta) 478 if err != nil { 479 klog.Errorf("update meta failed, %s", msgDebugInfo(&resp)) 480 } 481 resp.BuildHeader(resp.GetID(), originalID, resp.GetTimestamp()) 482 if resType == constants.ResourceTypeService || resType == constants.ResourceTypeEndpoints { 483 sendToEdgeMesh(&resp, message.IsSync()) 484 } else { 485 sendToEdged(&resp, message.IsSync()) 486 } 487 488 respToCloud := message.NewRespByMessage(&resp, OK) 489 sendToCloud(respToCloud) 490 }() 491 } 492 493 func (m *metaManager) processNodeConnection(message model.Message) { 494 content, _ := message.GetContent().(string) 495 klog.Infof("node connection event occur: %s", content) 496 if content == connect.CloudConnected { 497 metaManagerConfig.Connected = true 498 } else if content == connect.CloudDisconnected { 499 metaManagerConfig.Connected = false 500 } 501 } 502 503 func (m *metaManager) processSync(message model.Message) { 504 m.syncPodStatus() 505 } 506 507 func (m *metaManager) syncPodStatus() { 508 klog.Infof("start to sync pod status") 509 podStatusRecords, err := dao.QueryAllMeta("type", model.ResourceTypePodStatus) 510 if err != nil { 511 klog.Errorf("list pod status failed: %v", err) 512 return 513 } 514 if len(*podStatusRecords) <= 0 { 515 klog.Infof("list pod status, no record, skip sync") 516 return 517 } 518 519 var namespace string 520 content := make([]interface{}, 0, len(*podStatusRecords)) 521 for _, v := range *podStatusRecords { 522 if namespace == "" { 523 namespace, _, _, _ = util.ParseResourceEdge(v.Key, model.QueryOperation) 524 } 525 podKey := strings.Replace(v.Key, constants.ResourceSep+model.ResourceTypePodStatus+constants.ResourceSep, constants.ResourceSep+model.ResourceTypePod+constants.ResourceSep, 1) 526 podRecord, err := dao.QueryMeta("key", podKey) 527 if err != nil { 528 klog.Errorf("query pod[%s] failed: %v", podKey, err) 529 return 530 } 531 532 if len(*podRecord) <= 0 { 533 // pod already deleted, clear the corresponding podstatus record 534 err = dao.DeleteMetaByKey(v.Key) 535 klog.Infof("pod[%s] already deleted, clear podstatus record, result:%v", podKey, err) 536 continue 537 } 538 539 var podStatus interface{} 540 err = json.Unmarshal([]byte(v.Value), &podStatus) 541 if err != nil { 542 klog.Errorf("unmarshal podstatus[%s] failed, content[%s]: %v", v.Key, v.Value, err) 543 continue 544 } 545 content = append(content, podStatus) 546 } 547 548 msg := model.NewMessage("").BuildRouter(MetaManagerModuleName, GroupResource, namespace+constants.ResourceSep+model.ResourceTypePodStatus, model.UpdateOperation).FillBody(content) 549 sendToCloud(msg) 550 klog.Infof("sync pod status successful, %s", msgDebugInfo(msg)) 551 } 552 553 func (m *metaManager) processFunctionAction(message model.Message) { 554 555 var err error 556 var content []byte 557 switch message.GetContent().(type) { 558 case []uint8: 559 content = message.GetContent().([]byte) 560 default: 561 content, err = json.Marshal(message.GetContent()) 562 if err != nil { 563 klog.Errorf("marshal save message content failed, %s: %v", msgDebugInfo(&message), err) 564 feedbackError(err, "Error to marshal message content", message) 565 return 566 } 567 } 568 569 resKey, resType, _ := parseResource(message.GetResource()) 570 meta := &dao.Meta{ 571 Key: resKey, 572 Type: resType, 573 Value: string(content)} 574 err = dao.SaveMeta(meta) 575 if err != nil { 576 klog.Errorf("save meta failed, %s: %v", msgDebugInfo(&message), err) 577 feedbackError(err, "Error to save meta to DB", message) 578 return 579 } 580 581 beehiveContext.Send(EdgeFunctionModel, message) 582 } 583 584 func (m *metaManager) processFunctionActionResult(message model.Message) { 585 var err error 586 var content []byte 587 switch message.GetContent().(type) { 588 case []uint8: 589 content = message.GetContent().([]byte) 590 default: 591 content, err = json.Marshal(message.GetContent()) 592 if err != nil { 593 klog.Errorf("marshal save message content failed, %s: %v", msgDebugInfo(&message), err) 594 feedbackError(err, "Error to marshal message content", message) 595 return 596 } 597 } 598 599 resKey, resType, _ := parseResource(message.GetResource()) 600 meta := &dao.Meta{ 601 Key: resKey, 602 Type: resType, 603 Value: string(content)} 604 err = dao.SaveMeta(meta) 605 if err != nil { 606 klog.Errorf("save meta failed, %s: %v", msgDebugInfo(&message), err) 607 feedbackError(err, "Error to save meta to DB", message) 608 return 609 } 610 611 sendToCloud(&message) 612 613 } 614 615 func (m *metaManager) processVolume(message model.Message) { 616 klog.Info("process volume started") 617 back, err := beehiveContext.SendSync(modules.EdgedModuleName, message, constants.CSISyncMsgRespTimeout) 618 klog.Infof("process volume get: req[%+v], back[%+v], err[%+v]", message, back, err) 619 if err != nil { 620 klog.Errorf("process volume send to edged failed: %v", err) 621 } 622 623 resp := message.NewRespByMessage(&message, back.GetContent()) 624 sendToCloud(resp) 625 klog.Infof("process volume send to cloud resp[%+v]", resp) 626 } 627 628 func (m *metaManager) process(message model.Message) { 629 operation := message.GetOperation() 630 switch operation { 631 case model.InsertOperation: 632 m.processInsert(message) 633 case model.UpdateOperation: 634 m.processUpdate(message) 635 case model.DeleteOperation: 636 m.processDelete(message) 637 case model.QueryOperation: 638 m.processQuery(message) 639 case model.ResponseOperation: 640 m.processResponse(message) 641 case messagepkg.OperationNodeConnection: 642 m.processNodeConnection(message) 643 case OperationMetaSync: 644 m.processSync(message) 645 case OperationFunctionAction: 646 m.processFunctionAction(message) 647 case OperationFunctionActionResult: 648 m.processFunctionActionResult(message) 649 case constants.CSIOperationTypeCreateVolume, 650 constants.CSIOperationTypeDeleteVolume, 651 constants.CSIOperationTypeControllerPublishVolume, 652 constants.CSIOperationTypeControllerUnpublishVolume: 653 m.processVolume(message) 654 } 655 } 656 657 func (m *metaManager) runMetaManager() { 658 go func() { 659 for { 660 select { 661 case <-beehiveContext.Done(): 662 klog.Warning("MetaManager mainloop stop") 663 return 664 default: 665 666 } 667 if msg, err := beehiveContext.Receive(m.Name()); err == nil { 668 klog.Infof("get a message %+v", msg) 669 m.process(msg) 670 } else { 671 klog.Errorf("get a message %+v: %v", msg, err) 672 } 673 } 674 }() 675 }