├── CODE_OF_CONDUCT.md ├── LICENSE ├── Makefile ├── OWNERS ├── README.md ├── go.mod ├── go.sum ├── pkg ├── common │ └── type.go └── core │ ├── channel │ └── context_channel.go │ ├── context │ ├── context.go │ └── context_factory.go │ ├── core.go │ ├── model │ └── message.go │ ├── module.go │ └── socket │ ├── broker │ ├── broker.go │ └── broker_test.go │ ├── config │ └── config.go │ ├── context_socket.go │ ├── helper.go │ ├── modules │ └── socket │ │ ├── server.go │ │ └── socket.go │ ├── store │ ├── pipe.go │ └── pipeinfo.go │ ├── synckeeper │ ├── keeper.go │ └── keeper_test.go │ └── wrapper │ ├── packer │ └── packer.go │ ├── reader │ ├── package.go │ ├── raw.go │ └── reader.go │ ├── wrapper.go │ └── writer │ ├── package.go │ ├── raw.go │ └── writer.go └── test ├── cmd └── main.go └── modules ├── test_destination.go ├── test_destination_group.go └── test_source.go /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # KubeEdge Community Code of Conduct 2 | 3 | Please refer to our [KubeEdge Community Code of Conduct](https://github.com/kubeedge/community/blob/master/CODE_OF_CONDUCT.md) -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2019 The KubeEdge Authors. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | # 16 | 17 | 18 | PKG=github.com/kubeedge/beehive 19 | 20 | # Project packages. 21 | PACKAGES=$(shell go list ./... | grep -v "/vendor/") 22 | INTEGRATION_PACKAGE=${PKG} 23 | 24 | #Replaces ":" (*nix), ";" (windows) with newline for easy parsing 25 | GOPATHS=$(shell echo ${GOPATH} | tr ":" "\n" | tr ";" "\n") 26 | 27 | # Flags passed to `go test` 28 | TESTFLAGS ?= -v 29 | 30 | .PHONY: all test lint depcheck benchmark coverage 31 | 32 | .DEFAULT: default 33 | 34 | all: test lint depcheck ## default 35 | 36 | depcheck: ## Check if imports, Gopkg.toml, and Gopkg.lock are in sync 37 | dep check 38 | 39 | lint: ## golangci-lint check 40 | golangci-lint run --disable-all -E gofmt -E golint ./... 41 | go vet ./... 42 | 43 | test: ## test case 44 | @go test ${TESTFLAGS} $(filter-out ${INTEGRATION_PACKAGE},${PACKAGES}) 45 | 46 | # https://deepzz.com/post/study-golang-test.html 47 | # https://deepzz.com/post/the-command-flag-of-go-test.html 48 | benchmark: ## run benchmarks tests 49 | @go test ${TESTFLAGS} $(filter-out ${INTEGRATION_PACKAGE},${PACKAGES}) -bench . -run Benchmark 50 | 51 | coverage: ## generate coverprofiles from the unit tests, except tests that require root 52 | @rm -f coverage.txt 53 | @go test -i ${TESTFLAGS} $(filter-out ${INTEGRATION_PACKAGE},${PACKAGES}) 2> /dev/null 54 | @( for pkg in $(filter-out ${INTEGRATION_PACKAGE},${PACKAGES}); do \ 55 | go test ${TESTFLAGS} \ 56 | -cover \ 57 | -coverprofile=profile.out \ 58 | -covermode=atomic $$pkg || exit; \ 59 | if [ -f profile.out ]; then \ 60 | cat profile.out >> coverage.txt; \ 61 | $(RM) profile.out; \ 62 | fi; \ 63 | done ) 64 | -------------------------------------------------------------------------------- /OWNERS: -------------------------------------------------------------------------------- 1 | approvers: 2 | - kevin-wangzefeng 3 | - qizha 4 | - rohitsardesai83 5 | - sids-b 6 | - fisherxu 7 | reviewers: 8 | - kevin-wangzefeng 9 | - qizha 10 | - rohitsardesai83 11 | - sids-b 12 | - fisherxu 13 | - edisonxiang 14 | - kadisi 15 | - subpathdev 16 | - anyushun 17 | - Iceber 18 | - wackxu 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # beehive 2 | Communication Framework 3 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/kubeedge/beehive 2 | 3 | go 1.21 4 | 5 | require ( 6 | github.com/google/uuid v1.2.0 7 | k8s.io/klog/v2 v2.9.0 8 | sigs.k8s.io/yaml v1.2.0 9 | ) 10 | 11 | require ( 12 | github.com/go-logr/logr v0.4.0 // indirect 13 | gopkg.in/yaml.v2 v2.2.8 // indirect 14 | ) 15 | 16 | replace github.com/kubeedge/beehive => ../beehive 17 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 2 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 3 | github.com/go-logr/logr v0.4.0 h1:K7/B1jt6fIBQVd4Owv2MqGQClcgf0R266+7C/QjRcLc= 4 | github.com/go-logr/logr v0.4.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= 5 | github.com/google/uuid v1.2.0 h1:qJYtXnJRWmpe7m/3XlyhrsLrEURqHRM2kxzoxXqyUDs= 6 | github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 7 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 8 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 9 | gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= 10 | gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 11 | k8s.io/klog/v2 v2.9.0 h1:D7HV+n1V57XeZ0m6tdRkfknthUaM06VFbWldOFh8kzM= 12 | k8s.io/klog/v2 v2.9.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec= 13 | sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q= 14 | sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= 15 | -------------------------------------------------------------------------------- /pkg/common/type.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | // define channel type 4 | const ( 5 | // MsgCtxTypeChannel message type channel 6 | MsgCtxTypeChannel = "channel" 7 | // MsgCtxTypeUS message type us 8 | MsgCtxTypeUS = "unixpacket" 9 | 10 | // ResourceTypeModule resource type module 11 | ResourceTypeModule = "module" 12 | // OperationTypeModule operation type module 13 | OperationTypeModule = "add" 14 | ) 15 | 16 | // ModuleInfo is module info 17 | type ModuleInfo struct { 18 | ModuleName string 19 | ModuleType string 20 | // the below field ModuleSocket is only required for using socket. 21 | ModuleSocket 22 | } 23 | 24 | type ModuleSocket struct { 25 | IsRemote bool 26 | Connection interface{} // only for socket remote mode 27 | } 28 | -------------------------------------------------------------------------------- /pkg/core/channel/context_channel.go: -------------------------------------------------------------------------------- 1 | package channel 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | "sync/atomic" 7 | "time" 8 | 9 | "k8s.io/klog/v2" 10 | 11 | "github.com/kubeedge/beehive/pkg/common" 12 | "github.com/kubeedge/beehive/pkg/core/model" 13 | ) 14 | 15 | // constants for channel context 16 | const ( 17 | ChannelSizeDefault = 1024 18 | 19 | MessageTimeoutDefault = 30 * time.Second 20 | 21 | TickerTimeoutDefault = 20 * time.Millisecond 22 | ) 23 | 24 | // Context is object for Context channel 25 | type Context struct { 26 | //ConfigFactory goarchaius.ConfigurationFactory 27 | channels map[string]chan model.Message 28 | chsLock sync.RWMutex 29 | typeChannels map[string]map[string]chan model.Message 30 | typeChsLock sync.RWMutex 31 | anonChannels map[string]chan model.Message 32 | anonChsLock sync.RWMutex 33 | } 34 | 35 | var channelContext *Context 36 | var once sync.Once 37 | 38 | // NewChannelContext creates and returns object of new channel context 39 | func NewChannelContext() *Context { 40 | once.Do(func() { 41 | channelMap := make(map[string]chan model.Message) 42 | moduleChannels := make(map[string]map[string]chan model.Message) 43 | anonChannels := make(map[string]chan model.Message) 44 | channelContext = &Context{ 45 | channels: channelMap, 46 | typeChannels: moduleChannels, 47 | anonChannels: anonChannels, 48 | } 49 | }) 50 | return channelContext 51 | } 52 | 53 | // Cleanup close modules 54 | func (ctx *Context) Cleanup(module string) { 55 | if channel := ctx.getChannel(module); channel != nil { 56 | ctx.delChannel(module) 57 | // decrease probable exception of channel closing 58 | time.Sleep(20 * time.Millisecond) 59 | close(channel) 60 | } 61 | } 62 | 63 | // Send send msg to a module. Todo: do not stuck 64 | func (ctx *Context) Send(module string, message model.Message) { 65 | // avoid exception because of channel closing 66 | // TODO: need reconstruction 67 | defer func() { 68 | if exception := recover(); exception != nil { 69 | klog.Warningf("Recover when send message, exception: %+v", exception) 70 | } 71 | }() 72 | 73 | if channel := ctx.getChannel(module); channel != nil { 74 | channel <- message 75 | return 76 | } 77 | klog.Warningf("Get bad module name :%s when send message, do nothing", module) 78 | } 79 | 80 | // Receive msg from channel of module 81 | func (ctx *Context) Receive(module string) (model.Message, error) { 82 | if channel := ctx.getChannel(module); channel != nil { 83 | content := <-channel 84 | return content, nil 85 | } 86 | 87 | klog.Warningf("Failed to get channel for module:%s when receive message", module) 88 | return model.Message{}, fmt.Errorf("failed to get channel for module(%s)", module) 89 | } 90 | 91 | func getAnonChannelName(msgID string) string { 92 | return msgID 93 | } 94 | 95 | // SendSync sends message in a sync way 96 | func (ctx *Context) SendSync(module string, message model.Message, timeout time.Duration) (model.Message, error) { 97 | // avoid exception because of channel closing 98 | // TODO: need reconstruction 99 | defer func() { 100 | if exception := recover(); exception != nil { 101 | klog.Warningf("Recover when sendsync message, exception: %+v", exception) 102 | } 103 | }() 104 | 105 | if timeout <= 0 { 106 | timeout = MessageTimeoutDefault 107 | } 108 | deadline := time.Now().Add(timeout) 109 | 110 | // make sure to set sync flag 111 | message.Header.Sync = true 112 | 113 | // check req/resp channel 114 | reqChannel := ctx.getChannel(module) 115 | if reqChannel == nil { 116 | return model.Message{}, fmt.Errorf("bad request module name(%s)", module) 117 | } 118 | 119 | // new anonymous channel for response 120 | anonChan := make(chan model.Message) 121 | anonName := getAnonChannelName(message.GetID()) 122 | ctx.anonChsLock.Lock() 123 | ctx.anonChannels[anonName] = anonChan 124 | ctx.anonChsLock.Unlock() 125 | defer func() { 126 | ctx.anonChsLock.Lock() 127 | delete(ctx.anonChannels, anonName) 128 | close(anonChan) 129 | ctx.anonChsLock.Unlock() 130 | }() 131 | 132 | select { 133 | case reqChannel <- message: 134 | case <-time.After(timeout): 135 | return model.Message{}, fmt.Errorf("timeout to send message %s", message.GetID()) 136 | } 137 | 138 | var resp model.Message 139 | select { 140 | case resp = <-anonChan: 141 | case <-time.After(time.Until(deadline)): 142 | return model.Message{}, fmt.Errorf("timeout to get response for message %s", message.GetID()) 143 | } 144 | 145 | return resp, nil 146 | } 147 | 148 | // SendResp send resp for this message when using sync mode 149 | func (ctx *Context) SendResp(message model.Message) { 150 | anonName := getAnonChannelName(message.GetParentID()) 151 | 152 | ctx.anonChsLock.RLock() 153 | defer ctx.anonChsLock.RUnlock() 154 | if channel, exist := ctx.anonChannels[anonName]; exist { 155 | select { 156 | case channel <- message: 157 | default: 158 | klog.Warningf("no goroutine is ready for receive the message from "+ 159 | "unbuffered response channel, discard this resp message for %s", message.GetParentID()) 160 | } 161 | return 162 | } 163 | 164 | klog.Warningf("Get bad anonName:%s when sendresp message, do nothing", anonName) 165 | } 166 | 167 | // SendToGroup send msg to modules. Todo: do not stuck 168 | func (ctx *Context) SendToGroup(moduleType string, message model.Message) { 169 | send := func(module string, ch chan model.Message) { 170 | // avoid exception because of channel closing 171 | // TODO: need reconstruction 172 | defer func() { 173 | if exception := recover(); exception != nil { 174 | klog.Warningf("Recover when sendToGroup message, exception: %+v", exception) 175 | } 176 | }() 177 | select { 178 | case ch <- message: 179 | default: 180 | klog.Warningf("The module %s message channel is full, message: %+v", module, message) 181 | ch <- message 182 | } 183 | } 184 | if channelList := ctx.getTypeChannel(moduleType); channelList != nil { 185 | for module, channel := range channelList { 186 | go send(module, channel) 187 | } 188 | return 189 | } 190 | klog.Warningf("Get bad module type:%s when sendToGroup message, do nothing", moduleType) 191 | } 192 | 193 | // SendToGroupSync : broadcast the message to echo module channel, the module send response back anon channel 194 | // check timeout and the size of anon channel 195 | func (ctx *Context) SendToGroupSync(moduleType string, message model.Message, timeout time.Duration) error { 196 | if timeout <= 0 { 197 | timeout = MessageTimeoutDefault 198 | } 199 | deadline := time.Now().Add(timeout) 200 | 201 | channelList := ctx.getTypeChannel(moduleType) 202 | if channelList == nil { 203 | return fmt.Errorf("failed to get module type(%s) channel list", moduleType) 204 | } 205 | 206 | // each module must sync a response, 207 | // let anonchan size be module number 208 | channelNumber := len(channelList) 209 | anonChan := make(chan model.Message, channelNumber) 210 | anonName := getAnonChannelName(message.GetID()) 211 | ctx.anonChsLock.Lock() 212 | ctx.anonChannels[anonName] = anonChan 213 | ctx.anonChsLock.Unlock() 214 | 215 | cleanup := func() error { 216 | ctx.anonChsLock.Lock() 217 | delete(ctx.anonChannels, anonName) 218 | close(anonChan) 219 | ctx.anonChsLock.Unlock() 220 | 221 | var uninvitedGuests int 222 | // cleanup anonchan and check parentid for resp 223 | for resp := range anonChan { 224 | if resp.GetParentID() != message.GetID() { 225 | uninvitedGuests++ 226 | } 227 | } 228 | if uninvitedGuests != 0 { 229 | klog.Errorf("Get some unexpected:%d resp when sendToGroupsync message", uninvitedGuests) 230 | return fmt.Errorf("got some unexpected(%d) resp", uninvitedGuests) 231 | } 232 | return nil 233 | } 234 | 235 | // make sure to set sync flag before sending 236 | message.Header.Sync = true 237 | 238 | var timeoutCounter int32 239 | send := func(ch chan model.Message) { 240 | // avoid exception because of channel closing 241 | // TODO: need reconstruction 242 | defer func() { 243 | if exception := recover(); exception != nil { 244 | klog.Warningf("Recover when sendToGroupsync message, exception: %+v", exception) 245 | } 246 | }() 247 | sendTimer := time.NewTimer(time.Until(deadline)) 248 | select { 249 | case ch <- message: 250 | sendTimer.Stop() 251 | case <-sendTimer.C: 252 | atomic.AddInt32(&timeoutCounter, 1) 253 | } 254 | } 255 | for _, channel := range channelList { 256 | go send(channel) 257 | } 258 | 259 | sendTimer := time.NewTimer(time.Until(deadline)) 260 | ticker := time.NewTicker(TickerTimeoutDefault) 261 | for { 262 | // annonChan is full 263 | if len(anonChan) == channelNumber { 264 | break 265 | } 266 | select { 267 | case <-ticker.C: 268 | case <-sendTimer.C: 269 | err := cleanup() 270 | if err != nil { 271 | klog.Errorf("Failed to cleanup, error: %v", err) 272 | } 273 | if timeoutCounter != 0 { 274 | return fmt.Errorf("timeout to send message, several %d timeout when send", timeoutCounter) 275 | } 276 | klog.Error("Timeout to sendToGroupsync message") 277 | return fmt.Errorf("timeout to send message") 278 | } 279 | } 280 | 281 | return cleanup() 282 | } 283 | 284 | // New Channel 285 | func (ctx *Context) newChannel() chan model.Message { 286 | channel := make(chan model.Message, ChannelSizeDefault) 287 | return channel 288 | } 289 | 290 | // getChannel return chan 291 | func (ctx *Context) getChannel(module string) chan model.Message { 292 | ctx.chsLock.RLock() 293 | defer ctx.chsLock.RUnlock() 294 | 295 | if _, exist := ctx.channels[module]; exist { 296 | return ctx.channels[module] 297 | } 298 | 299 | klog.Warningf("Failed to get channel for module:%s", module) 300 | return nil 301 | } 302 | 303 | // addChannel return chan 304 | func (ctx *Context) addChannel(module string, moduleCh chan model.Message) { 305 | ctx.chsLock.Lock() 306 | defer ctx.chsLock.Unlock() 307 | 308 | ctx.channels[module] = moduleCh 309 | } 310 | 311 | // deleteChannel by module name 312 | func (ctx *Context) delChannel(module string) { 313 | // delete module channel from channels map 314 | ctx.chsLock.Lock() 315 | if _, exist := ctx.channels[module]; !exist { 316 | ctx.chsLock.Unlock() 317 | klog.Warningf("Failed to get channel, module:%s", module) 318 | return 319 | } 320 | delete(ctx.channels, module) 321 | ctx.chsLock.Unlock() 322 | 323 | // delete module channel from typechannels map 324 | ctx.typeChsLock.Lock() 325 | for _, moduleMap := range ctx.typeChannels { 326 | if _, exist := moduleMap[module]; exist { 327 | delete(moduleMap, module) 328 | break 329 | } 330 | } 331 | ctx.typeChsLock.Unlock() 332 | } 333 | 334 | // getTypeChannel return chan 335 | func (ctx *Context) getTypeChannel(moduleType string) map[string]chan model.Message { 336 | ctx.typeChsLock.RLock() 337 | defer ctx.typeChsLock.RUnlock() 338 | 339 | if v, exist := ctx.typeChannels[moduleType]; exist { 340 | return v 341 | } 342 | 343 | klog.Warningf("Failed to get type channel, type:%s", moduleType) 344 | return nil 345 | } 346 | 347 | func (ctx *Context) getModuleByChannel(ch chan model.Message) string { 348 | ctx.chsLock.RLock() 349 | defer ctx.chsLock.RUnlock() 350 | 351 | for module, channel := range ctx.channels { 352 | if channel == ch { 353 | return module 354 | } 355 | } 356 | 357 | klog.Warning("Failed to get module by channel") 358 | return "" 359 | } 360 | 361 | // addTypeChannel put modules into moduleType map 362 | func (ctx *Context) addTypeChannel(module, group string, moduleCh chan model.Message) { 363 | ctx.typeChsLock.Lock() 364 | defer ctx.typeChsLock.Unlock() 365 | 366 | if _, exist := ctx.typeChannels[group]; !exist { 367 | ctx.typeChannels[group] = make(map[string]chan model.Message) 368 | } 369 | ctx.typeChannels[group][module] = moduleCh 370 | } 371 | 372 | // AddModule adds module into module context 373 | func (ctx *Context) AddModule(info *common.ModuleInfo) { 374 | channel := ctx.newChannel() 375 | ctx.addChannel(info.ModuleName, channel) 376 | } 377 | 378 | // AddModuleGroup adds modules into module context group 379 | func (ctx *Context) AddModuleGroup(module, group string) { 380 | if channel := ctx.getChannel(module); channel != nil { 381 | ctx.addTypeChannel(module, group, channel) 382 | return 383 | } 384 | klog.Warningf("Get bad module name %s when addmodulegroup", module) 385 | } 386 | -------------------------------------------------------------------------------- /pkg/core/context/context.go: -------------------------------------------------------------------------------- 1 | package context 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/kubeedge/beehive/pkg/common" 7 | "github.com/kubeedge/beehive/pkg/core/model" 8 | ) 9 | 10 | // ModuleContext is interface for context module management 11 | type ModuleContext interface { 12 | AddModule(info *common.ModuleInfo) 13 | AddModuleGroup(module, group string) 14 | Cleanup(module string) 15 | } 16 | 17 | // MessageContext is interface for message syncing 18 | type MessageContext interface { 19 | // async mode 20 | Send(module string, message model.Message) 21 | Receive(module string) (model.Message, error) 22 | // sync mode 23 | SendSync(module string, message model.Message, timeout time.Duration) (model.Message, error) 24 | SendResp(message model.Message) 25 | // group broadcast 26 | SendToGroup(group string, message model.Message) 27 | SendToGroupSync(group string, message model.Message, timeout time.Duration) error 28 | } 29 | -------------------------------------------------------------------------------- /pkg/core/context/context_factory.go: -------------------------------------------------------------------------------- 1 | package context 2 | 3 | import ( 4 | gocontext "context" 5 | "fmt" 6 | "sync" 7 | "time" 8 | 9 | "k8s.io/klog/v2" 10 | 11 | "github.com/kubeedge/beehive/pkg/common" 12 | "github.com/kubeedge/beehive/pkg/core/channel" 13 | "github.com/kubeedge/beehive/pkg/core/model" 14 | "github.com/kubeedge/beehive/pkg/core/socket" 15 | ) 16 | 17 | // GlobalContext is global context: only use for local cache to dispatch message 18 | type GlobalContext struct { 19 | // context type(socket/channel) -> context 20 | moduleContext map[string]ModuleContext 21 | messageContext map[string]MessageContext 22 | 23 | // module name to context type 24 | moduleContextType map[string]string 25 | // group name to context type 26 | groupContextType map[string]string 27 | 28 | ctx gocontext.Context 29 | cancel gocontext.CancelFunc 30 | ctxLock sync.RWMutex 31 | } 32 | 33 | func init() { 34 | ctx, cancel := gocontext.WithCancel(gocontext.Background()) 35 | globalContext = &GlobalContext{ 36 | moduleContext: make(map[string]ModuleContext), 37 | messageContext: make(map[string]MessageContext), 38 | 39 | moduleContextType: make(map[string]string), 40 | groupContextType: make(map[string]string), 41 | 42 | ctx: ctx, 43 | cancel: cancel, 44 | } 45 | } 46 | 47 | var ( 48 | // singleton 49 | globalContext *GlobalContext 50 | once sync.Once 51 | ) 52 | 53 | // InitContext gets global context instance 54 | func InitContext(contextTypes []string) { 55 | for _, contextType := range contextTypes { 56 | switch contextType { 57 | case common.MsgCtxTypeChannel: 58 | channelContext := channel.NewChannelContext() 59 | globalContext.moduleContext[contextType] = channelContext 60 | globalContext.messageContext[contextType] = channelContext 61 | case common.MsgCtxTypeUS: 62 | socketContext := socket.InitSocketContext() 63 | globalContext.moduleContext[contextType] = socketContext 64 | globalContext.messageContext[contextType] = socketContext 65 | default: 66 | klog.Exitf("unsupported context type: %s", contextType) 67 | } 68 | } 69 | } 70 | 71 | func GetContext() gocontext.Context { 72 | return globalContext.ctx 73 | } 74 | 75 | func Done() <-chan struct{} { 76 | return globalContext.ctx.Done() 77 | } 78 | 79 | // AddModule adds module into module context 80 | func AddModule(module *common.ModuleInfo) { 81 | setModuleContextType(module.ModuleName, module.ModuleType) 82 | 83 | moduleContext, err := getModuleContext(module.ModuleName) 84 | if err != nil { 85 | klog.Errorf("failed to get module context, module name: %s, err: %v", module.ModuleName, err) 86 | return 87 | } 88 | 89 | moduleContext.AddModule(module) 90 | } 91 | 92 | // AddModuleGroup adds module into module context group 93 | func AddModuleGroup(module, group string) { 94 | setGroupContextType(module, group) 95 | 96 | moduleContext, err := getModuleContext(module) 97 | if err != nil { 98 | klog.Errorf("failed to get module context, module name: %s, err: %v", module, err) 99 | return 100 | } 101 | 102 | moduleContext.AddModuleGroup(module, group) 103 | } 104 | 105 | // Cancel function 106 | func Cancel() { 107 | globalContext.cancel() 108 | } 109 | 110 | // Cleanup cleans up module 111 | func Cleanup(module string) { 112 | moduleContext, err := getModuleContext(module) 113 | if err != nil { 114 | klog.Errorf("failed to get module context, module name: %s, err: %v", module, err) 115 | return 116 | } 117 | 118 | moduleContext.Cleanup(module) 119 | } 120 | 121 | // Send the message 122 | func Send(module string, message model.Message) { 123 | messageContext, err := getMessageContext(module) 124 | if err != nil { 125 | return 126 | } 127 | 128 | messageContext.Send(module, message) 129 | } 130 | 131 | // Receive the message 132 | // module : local module name 133 | func Receive(module string) (model.Message, error) { 134 | messageContext, err := getMessageContext(module) 135 | if err != nil { 136 | return model.Message{}, err 137 | } 138 | 139 | return messageContext.Receive(module) 140 | } 141 | 142 | // SendSync sends message in sync mode 143 | // module: the destination of the message 144 | // timeout: if <= 0 using default value(30s) 145 | func SendSync(module string, 146 | message model.Message, timeout time.Duration) (model.Message, error) { 147 | messageContext, err := getMessageContext(module) 148 | if err != nil { 149 | return model.Message{}, err 150 | } 151 | 152 | return messageContext.SendSync(module, message, timeout) 153 | } 154 | 155 | // SendResp sends response 156 | // please get resp message using model.NewRespByMessage 157 | func SendResp(resp model.Message) { 158 | messageContext, err := getMessageContextByMessageType(resp.GetType()) 159 | if err != nil { 160 | klog.Errorf("message context for module doesn't exist, module name: %s", resp.GetSource()) 161 | return 162 | } 163 | 164 | messageContext.SendResp(resp) 165 | } 166 | 167 | // SendToGroup broadcasts the message to all of group members 168 | func SendToGroup(group string, message model.Message) { 169 | messageContext, err := getMessageContextByGroup(group) 170 | if err != nil { 171 | klog.Errorf("message context for group doesn't exist, group name: %s", group) 172 | return 173 | } 174 | 175 | messageContext.SendToGroup(group, message) 176 | } 177 | 178 | // SendToGroupSync broadcasts the message to all of group members in sync mode 179 | func SendToGroupSync(group string, message model.Message, timeout time.Duration) error { 180 | messageContext, err := getMessageContextByGroup(group) 181 | if err != nil { 182 | return fmt.Errorf("message context for group doesn't exist, group name: %s", group) 183 | } 184 | 185 | return messageContext.SendToGroupSync(group, message, timeout) 186 | } 187 | 188 | func getModuleContext(moduleName string) (ModuleContext, error) { 189 | globalContext.ctxLock.RLock() 190 | defer globalContext.ctxLock.RUnlock() 191 | 192 | moduleContextType := getModuleContextType(moduleName) 193 | moduleContext, ok := globalContext.moduleContext[moduleContextType] 194 | if !ok { 195 | return nil, fmt.Errorf("module context %v doesn't exist", moduleContextType) 196 | } 197 | 198 | return moduleContext, nil 199 | } 200 | 201 | func getMessageContext(moduleName string) (MessageContext, error) { 202 | globalContext.ctxLock.RLock() 203 | defer globalContext.ctxLock.RUnlock() 204 | 205 | moduleContextType := getModuleContextType(moduleName) 206 | messageContext, ok := globalContext.messageContext[moduleContextType] 207 | if !ok { 208 | return nil, fmt.Errorf("message context %v doesn't exist", moduleContextType) 209 | } 210 | 211 | return messageContext, nil 212 | } 213 | 214 | func getMessageContextByMessageType(messageType string) (MessageContext, error) { 215 | if messageType == "" { 216 | messageType = common.MsgCtxTypeChannel 217 | } 218 | 219 | globalContext.ctxLock.RLock() 220 | defer globalContext.ctxLock.RUnlock() 221 | 222 | messageContext, ok := globalContext.messageContext[messageType] 223 | if !ok { 224 | return nil, fmt.Errorf("message context for message type doesn't exist, message type: %s", messageType) 225 | } 226 | 227 | return messageContext, nil 228 | } 229 | 230 | func getMessageContextByGroup(group string) (MessageContext, error) { 231 | globalContext.ctxLock.RLock() 232 | defer globalContext.ctxLock.RUnlock() 233 | 234 | contextType := globalContext.groupContextType[group] 235 | messageContext, ok := globalContext.messageContext[contextType] 236 | if !ok { 237 | return nil, fmt.Errorf("message context doesn't exist, group: %s, contextType: %s", group, contextType) 238 | } 239 | 240 | return messageContext, nil 241 | } 242 | 243 | // caller must lock the globalContext.ctxLock 244 | func getModuleContextType(moduleName string) string { 245 | return globalContext.moduleContextType[moduleName] 246 | } 247 | 248 | func setModuleContextType(moduleName string, contextType string) { 249 | globalContext.ctxLock.Lock() 250 | defer globalContext.ctxLock.Unlock() 251 | globalContext.moduleContextType[moduleName] = contextType 252 | } 253 | 254 | func setGroupContextType(module string, group string) { 255 | globalContext.ctxLock.Lock() 256 | defer globalContext.ctxLock.Unlock() 257 | 258 | globalContext.groupContextType[group] = globalContext.moduleContextType[module] 259 | } 260 | -------------------------------------------------------------------------------- /pkg/core/core.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "os" 5 | "os/signal" 6 | "syscall" 7 | "time" 8 | 9 | "k8s.io/klog/v2" 10 | 11 | "github.com/kubeedge/beehive/pkg/common" 12 | beehiveContext "github.com/kubeedge/beehive/pkg/core/context" 13 | ) 14 | 15 | // StartModules starts modules that are registered 16 | func StartModules() { 17 | // only register channel mode, if we want to use socket mode, we should also pass in common.MsgCtxTypeUS parameter 18 | beehiveContext.InitContext([]string{common.MsgCtxTypeChannel}) 19 | 20 | modules := GetModules() 21 | 22 | for name, module := range modules { 23 | var m common.ModuleInfo 24 | switch module.contextType { 25 | case common.MsgCtxTypeChannel: 26 | m = common.ModuleInfo{ 27 | ModuleName: name, 28 | ModuleType: module.contextType, 29 | } 30 | case common.MsgCtxTypeUS: 31 | m = common.ModuleInfo{ 32 | ModuleName: name, 33 | ModuleType: module.contextType, 34 | // the below field ModuleSocket is only required for using socket. 35 | ModuleSocket: common.ModuleSocket{ 36 | IsRemote: module.remote, 37 | }, 38 | } 39 | default: 40 | klog.Exitf("unsupported context type: %s", module.contextType) 41 | } 42 | 43 | beehiveContext.AddModule(&m) 44 | beehiveContext.AddModuleGroup(name, module.module.Group()) 45 | 46 | if module.remote { 47 | go moduleKeeper(name, module, m) 48 | } else { 49 | go localModuleKeeper(module) 50 | } 51 | 52 | klog.Infof("starting module %s", name) 53 | } 54 | } 55 | 56 | // GracefulShutdown is if it gets the special signals it does modules cleanup 57 | func GracefulShutdown() { 58 | c := make(chan os.Signal, 1) 59 | signal.Notify(c, syscall.SIGINT, syscall.SIGHUP, syscall.SIGTERM, 60 | syscall.SIGQUIT, syscall.SIGILL, syscall.SIGTRAP, syscall.SIGABRT) 61 | s := <-c 62 | klog.Infof("Get os signal %v", s.String()) 63 | 64 | // Cleanup each modules 65 | beehiveContext.Cancel() 66 | modules := GetModules() 67 | for name := range modules { 68 | klog.Infof("Cleanup module %v", name) 69 | beehiveContext.Cleanup(name) 70 | } 71 | } 72 | 73 | // Run starts the modules and in the end does module cleanup 74 | func Run() { 75 | // Address the module registration and start the core 76 | StartModules() 77 | // monitor system signal and shutdown gracefully 78 | GracefulShutdown() 79 | } 80 | 81 | func moduleKeeper(name string, moduleInfo *ModuleInfo, m common.ModuleInfo) { 82 | for { 83 | moduleInfo.module.Start() 84 | // local modules are always online 85 | if !moduleInfo.remote { 86 | return 87 | } 88 | // try to add module for remote modules 89 | beehiveContext.AddModule(&m) 90 | beehiveContext.AddModuleGroup(name, moduleInfo.module.Group()) 91 | } 92 | } 93 | 94 | // localModuleKeeper starts and tries to keep module running when module exited. 95 | // Call EnableModuleRestart() to enable auto-restarting feature in alpha version. 96 | func localModuleKeeper(m *ModuleInfo) { 97 | if !moduleRestartEnabled { 98 | m.module.Start() 99 | return 100 | } 101 | 102 | ctx := beehiveContext.GetContext() 103 | backoffDuration := time.Second 104 | 105 | // do if module exits 106 | afterFunc := func() { 107 | if r := recover(); r != nil { 108 | klog.Errorf("module %s panicking: %v", m.module.Name(), r) 109 | } 110 | klog.Errorf("module %s exited, will restart in %ds", m.module.Name(), int(backoffDuration.Seconds())) 111 | } 112 | 113 | for { 114 | func() { 115 | defer afterFunc() 116 | m.module.Start() 117 | }() 118 | 119 | select { 120 | case <-ctx.Done(): 121 | klog.Infof("module %s shutdown", m.module.Name()) 122 | return 123 | case <-time.After(backoffDuration): 124 | } 125 | 126 | if backoffDuration < 30*time.Second { 127 | backoffDuration *= 2 128 | if backoffDuration > 30*time.Second { 129 | backoffDuration = 30 * time.Second 130 | } 131 | } 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /pkg/core/model/message.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "fmt" 7 | "reflect" 8 | "time" 9 | 10 | "github.com/google/uuid" 11 | ) 12 | 13 | // Constants for database operations and resource type settings 14 | const ( 15 | InsertOperation = "insert" 16 | DeleteOperation = "delete" 17 | QueryOperation = "query" 18 | UpdateOperation = "update" 19 | PatchOperation = "patch" 20 | UploadOperation = "upload" 21 | ResponseOperation = "response" 22 | ResponseErrorOperation = "error" 23 | 24 | ResourceTypePod = "pod" 25 | ResourceTypeEvent = "event" 26 | ResourceTypeConfigmap = "configmap" 27 | ResourceTypeServiceAccountToken = "serviceaccounttoken" 28 | ResourceTypeSecret = "secret" 29 | ResourceTypeNode = "node" 30 | ResourceTypePodlist = "podlist" 31 | ResourceTypePodStatus = "podstatus" 32 | ResourceTypePodPatch = "podpatch" 33 | ResourceTypeNodeStatus = "nodestatus" 34 | ResourceTypeNodePatch = "nodepatch" 35 | ResourceTypeRule = "rule" 36 | ResourceTypeRuleEndpoint = "ruleendpoint" 37 | ResourceTypeRuleStatus = "rulestatus" 38 | ResourceTypeLease = "lease" 39 | ResourceTypeSaAccess = "serviceaccountaccess" 40 | ResourceTypeCSR = "certificatesigningrequest" 41 | 42 | ResourceTypeK8sCA = "k8s/ca.crt" 43 | ) 44 | 45 | // Message struct 46 | type Message struct { 47 | Header MessageHeader `json:"header"` 48 | Router MessageRoute `json:"route,omitempty"` 49 | Content interface{} `json:"content"` 50 | } 51 | 52 | // MessageRoute contains structure of message 53 | type MessageRoute struct { 54 | // where the message come from 55 | Source string `json:"source,omitempty"` 56 | // where the message will send to 57 | Destination string `json:"destination,omitempty"` 58 | // where the message will broadcast to 59 | Group string `json:"group,omitempty"` 60 | 61 | // what's the operation on resource 62 | Operation string `json:"operation,omitempty"` 63 | // what's the resource want to operate 64 | Resource string `json:"resource,omitempty"` 65 | } 66 | 67 | // MessageHeader defines message header details 68 | type MessageHeader struct { 69 | // the message uuid 70 | ID string `json:"msg_id"` 71 | // the response message parentid must be same with message received 72 | // please use NewRespByMessage to new response message 73 | ParentID string `json:"parent_msg_id,omitempty"` 74 | // the time of creating 75 | Timestamp int64 `json:"timestamp"` 76 | // specific resource version for the message, if any. 77 | // it's currently backed by resource version of the k8s object saved in the Content field. 78 | // kubeedge leverages the concept of message resource version to achieve reliable transmission. 79 | ResourceVersion string `json:"resourceversion,omitempty"` 80 | // the flag will be set in sendsync 81 | Sync bool `json:"sync,omitempty"` 82 | // message type indicates the context type that delivers the message, such as channel, unixsocket, etc. 83 | // if the value is empty, the channel context type will be used. 84 | MessageType string `json:"type,omitempty"` 85 | } 86 | 87 | // BuildRouter sets route and resource operation in message 88 | func (msg *Message) BuildRouter(source, group, res, opr string) *Message { 89 | msg.SetRoute(source, group) 90 | msg.SetResourceOperation(res, opr) 91 | return msg 92 | } 93 | 94 | // SetType set message context type 95 | func (msg *Message) SetType(msgType string) *Message { 96 | msg.Header.MessageType = msgType 97 | return msg 98 | } 99 | 100 | // SetDestination set destination 101 | func (msg *Message) SetDestination(dest string) *Message { 102 | msg.Router.Destination = dest 103 | return msg 104 | } 105 | 106 | // GetType get message context type 107 | func (msg *Message) GetType() string { 108 | return msg.Header.MessageType 109 | } 110 | 111 | // IsEmpty is empty 112 | func (msg *Message) IsEmpty() bool { 113 | return reflect.DeepEqual(msg, &Message{}) 114 | } 115 | 116 | // SetResourceOperation sets router resource and operation in message 117 | func (msg *Message) SetResourceOperation(res, opr string) *Message { 118 | msg.Router.Resource = res 119 | msg.Router.Operation = opr 120 | return msg 121 | } 122 | 123 | // SetRoute sets router source and group in message 124 | func (msg *Message) SetRoute(source, group string) *Message { 125 | msg.Router.Source = source 126 | msg.Router.Group = group 127 | return msg 128 | } 129 | 130 | // SetResourceVersion sets resource version in message header 131 | func (msg *Message) SetResourceVersion(resourceVersion string) *Message { 132 | msg.Header.ResourceVersion = resourceVersion 133 | return msg 134 | } 135 | 136 | // IsSync : msg.Header.Sync will be set in sendsync 137 | func (msg *Message) IsSync() bool { 138 | return msg.Header.Sync 139 | } 140 | 141 | // GetResource returns message route resource 142 | func (msg *Message) GetResource() string { 143 | return msg.Router.Resource 144 | } 145 | 146 | // GetOperation returns message route operation string 147 | func (msg *Message) GetOperation() string { 148 | return msg.Router.Operation 149 | } 150 | 151 | // GetSource returns message route source string 152 | func (msg *Message) GetSource() string { 153 | return msg.Router.Source 154 | } 155 | 156 | // GetGroup returns message route group 157 | func (msg *Message) GetGroup() string { 158 | return msg.Router.Group 159 | } 160 | 161 | // GetID returns message ID 162 | func (msg *Message) GetID() string { 163 | return msg.Header.ID 164 | } 165 | 166 | // GetParentID returns message parent id 167 | func (msg *Message) GetParentID() string { 168 | return msg.Header.ParentID 169 | } 170 | 171 | // GetTimestamp returns message timestamp 172 | func (msg *Message) GetTimestamp() int64 { 173 | return msg.Header.Timestamp 174 | } 175 | 176 | // GetContent returns message content 177 | func (msg *Message) GetContent() interface{} { 178 | return msg.Content 179 | } 180 | 181 | // GetContentData returns message content data 182 | func (msg *Message) GetContentData() ([]byte, error) { 183 | if data, ok := msg.Content.([]byte); ok { 184 | return data, nil 185 | } 186 | 187 | if data, ok := msg.Content.(string); ok { 188 | return []byte(data), nil 189 | } 190 | 191 | data, err := json.Marshal(msg.Content) 192 | if err != nil { 193 | return nil, fmt.Errorf("marshal message content failed: %s", err) 194 | } 195 | return data, nil 196 | } 197 | 198 | // GetResourceVersion returns message resource version 199 | func (msg *Message) GetResourceVersion() string { 200 | return msg.Header.ResourceVersion 201 | } 202 | 203 | // UpdateID returns message object updating its ID 204 | func (msg *Message) UpdateID() *Message { 205 | msg.Header.ID = uuid.New().String() 206 | return msg 207 | } 208 | 209 | // BuildHeader builds message header. You can also use for updating message header 210 | func (msg *Message) BuildHeader(ID, parentID string, timestamp int64) *Message { 211 | msg.Header.ID = ID 212 | msg.Header.ParentID = parentID 213 | msg.Header.Timestamp = timestamp 214 | return msg 215 | } 216 | 217 | // FillBody fills message content that you want to send 218 | func (msg *Message) FillBody(content interface{}) *Message { 219 | msg.Content = content 220 | return msg 221 | } 222 | 223 | // NewRawMessage returns a new raw message: 224 | // model.NewRawMessage().BuildHeader().BuildRouter().FillBody() 225 | func NewRawMessage() *Message { 226 | return &Message{} 227 | } 228 | 229 | // NewMessage returns a new basic message: 230 | // model.NewMessage().BuildRouter().FillBody() 231 | func NewMessage(parentID string) *Message { 232 | msg := &Message{} 233 | msg.Header.ID = uuid.New().String() 234 | msg.Header.ParentID = parentID 235 | msg.Header.Timestamp = time.Now().UnixNano() / 1e6 236 | return msg 237 | } 238 | 239 | // Clone a message 240 | // only update message id 241 | func (msg *Message) Clone(message *Message) *Message { 242 | msgID := uuid.New().String() 243 | return NewRawMessage().BuildHeader(msgID, message.GetParentID(), message.GetTimestamp()). 244 | BuildRouter(message.GetSource(), message.GetGroup(), message.GetResource(), message.GetOperation()). 245 | FillBody(message.GetContent()) 246 | } 247 | 248 | // NewRespByMessage returns a new response message by a message received 249 | func (msg *Message) NewRespByMessage(message *Message, content interface{}) *Message { 250 | return NewMessage(message.GetID()).SetRoute(message.GetSource(), message.GetGroup()). 251 | SetResourceOperation(message.GetResource(), ResponseOperation). 252 | SetType(message.GetType()). 253 | FillBody(content) 254 | } 255 | 256 | // NewErrorMessage returns a new error message by a message received 257 | func NewErrorMessage(message *Message, errContent string) *Message { 258 | return NewMessage(message.GetID()). 259 | SetResourceOperation(message.Router.Resource, ResponseErrorOperation). 260 | FillBody(errContent) 261 | } 262 | 263 | // GetDestination get destination 264 | func (msg *Message) GetDestination() string { 265 | return msg.Router.Destination 266 | } 267 | 268 | // String the content that you want to send 269 | func (msg *Message) String() string { 270 | var buffer bytes.Buffer 271 | buffer.WriteString("MessageID: " + msg.GetID()) 272 | buffer.WriteString(" ParentID: " + msg.GetParentID()) 273 | buffer.WriteString(" Group: " + msg.GetGroup()) 274 | buffer.WriteString(" Source: " + msg.GetSource()) 275 | buffer.WriteString(" Destination: " + msg.GetDestination()) 276 | buffer.WriteString(" Resource: " + msg.GetResource()) 277 | buffer.WriteString(" Operation: " + msg.GetOperation()) 278 | return buffer.String() 279 | } 280 | -------------------------------------------------------------------------------- /pkg/core/module.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "k8s.io/klog/v2" 5 | 6 | "github.com/kubeedge/beehive/pkg/common" 7 | "github.com/kubeedge/beehive/pkg/core/socket" 8 | ) 9 | 10 | // Module interface 11 | type Module interface { 12 | Name() string 13 | Group() string 14 | Start() 15 | Enable() bool 16 | } 17 | 18 | var ( 19 | // Modules map 20 | modules map[string]*ModuleInfo 21 | disabledModules map[string]*ModuleInfo 22 | // feature gates 23 | moduleRestartEnabled bool 24 | ) 25 | 26 | func init() { 27 | modules = make(map[string]*ModuleInfo) 28 | disabledModules = make(map[string]*ModuleInfo) 29 | } 30 | 31 | // ModuleInfo represent a module info 32 | type ModuleInfo struct { 33 | contextType string 34 | remote bool 35 | module Module 36 | } 37 | 38 | // Register register module 39 | // if not passed in parameter opts, default contextType is "channel" 40 | func Register(m Module, opts ...string) { 41 | info := &ModuleInfo{ 42 | module: m, 43 | contextType: common.MsgCtxTypeChannel, 44 | remote: false, 45 | } 46 | 47 | if len(opts) > 0 { 48 | info.contextType = opts[0] 49 | info.remote = true 50 | } 51 | 52 | if m.Enable() { 53 | modules[m.Name()] = info 54 | klog.Infof("Module %s registered successfully", m.Name()) 55 | } else { 56 | disabledModules[m.Name()] = info 57 | klog.Warningf("Module %v is disabled, do not register", m.Name()) 58 | } 59 | } 60 | 61 | // GetModules gets modules map 62 | func GetModules() map[string]*ModuleInfo { 63 | return modules 64 | } 65 | 66 | // GetModule gets module 67 | func (m *ModuleInfo) GetModule() Module { 68 | return m.module 69 | } 70 | 71 | // GetModuleExchange return module exchange 72 | func GetModuleExchange() *socket.ModuleExchange { 73 | exchange := socket.ModuleExchange{ 74 | Groups: make(map[string][]string), 75 | } 76 | for name, moduleInfo := range modules { 77 | exchange.Modules = append(exchange.Modules, name) 78 | group := moduleInfo.module.Group() 79 | exchange.Groups[group] = append(exchange.Groups[group], name) 80 | } 81 | return &exchange 82 | } 83 | 84 | // EnableModuleRestart enable feature for auto restarting modules 85 | func EnableModuleRestart() { 86 | moduleRestartEnabled = true 87 | } 88 | 89 | // IsModuleRestartEnabled checks whether auto-restart feature is enabled. 90 | func IsModuleRestartEnabled() bool { 91 | return moduleRestartEnabled 92 | } 93 | -------------------------------------------------------------------------------- /pkg/core/socket/broker/broker.go: -------------------------------------------------------------------------------- 1 | package broker 2 | 3 | import ( 4 | "crypto/tls" 5 | "fmt" 6 | "net/http" 7 | "time" 8 | 9 | "k8s.io/klog/v2" 10 | 11 | "github.com/kubeedge/beehive/pkg/core/model" 12 | "github.com/kubeedge/beehive/pkg/core/socket/synckeeper" 13 | "github.com/kubeedge/beehive/pkg/core/socket/wrapper" 14 | ) 15 | 16 | const ( 17 | syncMessageTimeoutDefault = 10 * time.Second 18 | ) 19 | 20 | // RemoteBroker remote broker 21 | type RemoteBroker struct { 22 | keeper *synckeeper.Keeper 23 | } 24 | 25 | // ConnectOptions connect options 26 | type ConnectOptions struct { 27 | Address string 28 | MessageType string 29 | BufferSize int 30 | Cert tls.Certificate 31 | 32 | // for websocket/http 33 | RequestHeader http.Header 34 | } 35 | 36 | // ConnectFunc connect func 37 | type ConnectFunc func(ConnectOptions) (interface{}, error) 38 | 39 | // NewRemoteBroker new remote broker 40 | func NewRemoteBroker() *RemoteBroker { 41 | return &RemoteBroker{ 42 | keeper: synckeeper.NewKeeper(), 43 | } 44 | } 45 | 46 | // Connect connect 47 | func (broker *RemoteBroker) Connect(opts ConnectOptions, connect ConnectFunc) wrapper.Conn { 48 | conn, err := connect(opts) 49 | if err != nil { 50 | klog.Errorf("failed to connect, address: %s; error:%+v", opts.Address, err) 51 | return nil 52 | } 53 | return wrapper.NewWrapper(opts.MessageType, conn, opts.BufferSize) 54 | } 55 | 56 | // Send send 57 | func (broker *RemoteBroker) Send(conn wrapper.Conn, message model.Message) error { 58 | //log.LOGGER.Infof("connection: %+v message: %+v", conn, message) 59 | err := conn.WriteJSON(&message) 60 | if err != nil { 61 | klog.Errorf("failed to write with error %+v", err) 62 | return fmt.Errorf("failed to write, error: %+v", err) 63 | } 64 | return nil 65 | } 66 | 67 | // Receive receive 68 | func (broker *RemoteBroker) Receive(conn wrapper.Conn) (model.Message, error) { 69 | var message model.Message 70 | for { 71 | err := conn.SetReadDeadline(time.Time{}) 72 | err = conn.ReadJSON(&message) 73 | if err != nil { 74 | klog.Errorf("failed to read, error:%+v", err) 75 | return model.Message{}, fmt.Errorf("failed to read, error: %+v", err) 76 | } 77 | 78 | isResponse := broker.keeper.IsSyncResponse(message.GetParentID()) 79 | if !isResponse { 80 | return message, nil 81 | } 82 | 83 | err = broker.keeper.SendToKeepChannel(message) 84 | } 85 | } 86 | 87 | // SendSyncInternal sync mode 88 | func (broker *RemoteBroker) SendSyncInternal(conn wrapper.Conn, message model.Message, timeout time.Duration) (model.Message, error) { 89 | if timeout <= 0 { 90 | timeout = syncMessageTimeoutDefault 91 | } 92 | 93 | // make sure to set sync flag 94 | message.Header.Sync = true 95 | 96 | err := conn.WriteJSON(&message) 97 | if err != nil { 98 | klog.Errorf("failed to write with error %+v", err) 99 | return model.Message{}, fmt.Errorf("failed to write, error: %+v", err) 100 | } 101 | 102 | deadline := time.Now().Add(timeout) 103 | err = conn.SetReadDeadline(deadline) 104 | var response model.Message 105 | err = conn.ReadJSON(&response) 106 | if err != nil { 107 | klog.Errorf("failed to read with error %+v", err) 108 | return model.Message{}, fmt.Errorf("failed to read, error: %+v", err) 109 | } 110 | 111 | return response, nil 112 | } 113 | 114 | // SendSync sync mode 115 | func (broker *RemoteBroker) SendSync(conn wrapper.Conn, message model.Message, timeout time.Duration) (model.Message, error) { 116 | if timeout <= 0 { 117 | timeout = syncMessageTimeoutDefault 118 | } 119 | 120 | deadline := time.Now().Add(timeout) 121 | 122 | // make sure to set sync flag 123 | message.Header.Sync = true 124 | 125 | err := conn.WriteJSON(&message) 126 | if err != nil { 127 | klog.Errorf("failed to write with error %+v", err) 128 | return model.Message{}, fmt.Errorf("failed to write, error: %+v", err) 129 | } 130 | 131 | tempChannel := broker.keeper.AddKeepChannel(message.GetID()) 132 | sendTimer := time.NewTimer(time.Until(deadline)) 133 | select { 134 | case response := <-tempChannel: 135 | sendTimer.Stop() 136 | broker.keeper.DeleteKeepChannel(response.GetParentID()) 137 | return response, nil 138 | case <-sendTimer.C: 139 | klog.Warningf("timeout to receive response for message: %s", message.String()) 140 | broker.keeper.DeleteKeepChannel(message.GetID()) 141 | return model.Message{}, fmt.Errorf("timeout to receive response for message: %s", message.String()) 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /pkg/core/socket/broker/broker_test.go: -------------------------------------------------------------------------------- 1 | package broker 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | "os" 7 | "strings" 8 | "testing" 9 | "time" 10 | 11 | "k8s.io/klog/v2" 12 | 13 | "github.com/kubeedge/beehive/pkg/core/model" 14 | "github.com/kubeedge/beehive/pkg/core/socket/wrapper" 15 | ) 16 | 17 | func serveSocket(socketType, address string, handle func(conn wrapper.Conn)) error { 18 | if strings.Contains(socketType, "unix") { 19 | os.Remove(address) 20 | } 21 | 22 | listener, err := net.Listen(socketType, address) 23 | defer func() { 24 | if listener != nil { 25 | err := listener.Close() 26 | if err != nil { 27 | klog.Errorf("listener close err %+v", err) 28 | } 29 | } 30 | }() 31 | if err != nil { 32 | klog.Errorf("failed to listen to unix domain socket with error %+v", err) 33 | return fmt.Errorf("failed to listen to unix domain socket, error: %+v ", err) 34 | } 35 | klog.Infof("Listening on addr: %s", listener.Addr().String()) 36 | 37 | conn, err := listener.Accept() 38 | klog.Infof("Connected from %s", conn.LocalAddr().String()) 39 | if err != nil { 40 | klog.Errorf("failed to accept with error %+v", err) 41 | return fmt.Errorf("failed to accept, error: %+v", err) 42 | } 43 | 44 | wrapper := wrapper.NewWrapper(socketType, conn, 10240) 45 | handle(wrapper) 46 | err = conn.Close() 47 | 48 | return err 49 | } 50 | 51 | // SocketConnect socket connect 52 | func SocketConnect(opts ConnectOptions) (interface{}, error) { 53 | conn, err := net.Dial(opts.MessageType, opts.Address) 54 | if err != nil { 55 | klog.Errorf("failed to dail addrs %s, error:%+v", opts.Address, err) 56 | return nil, err 57 | } 58 | return conn, nil 59 | } 60 | 61 | // TestMessageBroker_SendReceive test message broker_ send receive 62 | func TestMessageBroker_SendReceive(t *testing.T) { 63 | broker := NewRemoteBroker() 64 | stopChan := make(chan struct{}) 65 | handle := func(conn wrapper.Conn) { 66 | message, err := broker.Receive(conn) 67 | if err != nil { 68 | t.Fatalf("failed to receive message, error: %+v", err) 69 | } 70 | fmt.Printf("recive message: %+v", message) 71 | if message.GetContent() != "hello" { 72 | t.Fatalf("bad message content") 73 | } 74 | stopChan <- struct{}{} 75 | } 76 | go func() { 77 | err := serveSocket("tcp", "127.0.0.1:1234", handle) 78 | if err != nil { 79 | t.Errorf("failed to serve socket") 80 | return 81 | } 82 | }() 83 | 84 | time.Sleep(1 * time.Second) 85 | brokerClient := NewRemoteBroker() 86 | 87 | opts := ConnectOptions{ 88 | Address: "127.0.0.1:1234", 89 | MessageType: "tcp", 90 | BufferSize: 10240, 91 | } 92 | conn := brokerClient.Connect(opts, SocketConnect) 93 | if conn == nil { 94 | t.Fatalf("failed to connect tcp") 95 | } 96 | _ = brokerClient.Send(conn, *model.NewMessage("").FillBody("hello")) 97 | 98 | select { 99 | case _, ok := <-time.After(syncMessageTimeoutDefault): 100 | if ok { 101 | t.Fatalf("time out ti recive message") 102 | } 103 | case _, ok := <-stopChan: 104 | if ok { 105 | klog.Warningf("channel stopped") 106 | } 107 | } 108 | conn.Close() 109 | } 110 | 111 | // TestMessageBroker_SendSync test message broker_ send sync 112 | func TestMessageBroker_SendSync(t *testing.T) { 113 | brokerServer := NewRemoteBroker() 114 | stopTChan := make(chan struct{}) 115 | handle := func(conn wrapper.Conn) { 116 | message, err := brokerServer.Receive(conn) 117 | if err != nil { 118 | t.Fatalf("failed to receive message, error: %+v", err) 119 | } 120 | if message.GetContent() != "hello" { 121 | t.Fatalf("bad message content") 122 | } 123 | resp := message.NewRespByMessage(&message, "hello_response") 124 | _ = brokerServer.Send(conn, *resp) 125 | } 126 | go func() { 127 | err := serveSocket("tcp", "127.0.0.1:1234", handle) 128 | if err != nil { 129 | t.Errorf("failed to serve socket") 130 | return 131 | } 132 | }() 133 | 134 | time.Sleep(1 * time.Second) 135 | brokerClient := NewRemoteBroker() 136 | opts := ConnectOptions{ 137 | Address: "127.0.0.1:1234", 138 | MessageType: "tcp", 139 | BufferSize: 10240, 140 | } 141 | conn := brokerClient.Connect(opts, SocketConnect) 142 | if conn == nil { 143 | t.Fatalf("failed to connect tcp") 144 | } 145 | 146 | go func() { 147 | _, _ = brokerClient.Receive(conn) 148 | }() 149 | 150 | go func() { 151 | resp, err := brokerClient.SendSync(conn, *model.NewMessage(""). 152 | SetRoute("source", "dest").FillBody("hello"), 0) 153 | if err != nil { 154 | t.Errorf("failed to send sync message, error: %+v", err) 155 | return 156 | } 157 | if resp.GetContent() != "hello_response" { 158 | t.Errorf("unexpected receive message") 159 | return 160 | } 161 | stopTChan <- struct{}{} 162 | }() 163 | 164 | select { 165 | case _, ok := <-time.After(syncMessageTimeoutDefault): 166 | if ok { 167 | t.Fatalf("time out to send sync") 168 | } 169 | case _, ok := <-stopTChan: 170 | if ok { 171 | klog.Warningf("channel stopped") 172 | } 173 | } 174 | conn.Close() 175 | } 176 | -------------------------------------------------------------------------------- /pkg/core/socket/config/config.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | 7 | "k8s.io/klog/v2" 8 | "sigs.k8s.io/yaml" 9 | ) 10 | 11 | // SocketConfig socket config 12 | type SocketConfig struct { 13 | ModuleName string `json:"module"` 14 | Server bool `json:"server"` 15 | Address string `json:"address"` 16 | SocketType string `json:"sockettype,omitempty"` 17 | ConnNumberMax int `json:"connmax"` 18 | BufferSize int `json:"buffersize,omitempty"` 19 | CaRoot string `json:"ca,omitempty"` 20 | Cert string `json:"cert,omitempty"` 21 | Key string `json:"key,omitempty"` 22 | } 23 | 24 | // BuildinModuleConfig buildin module config 25 | type BuildinModuleConfig struct { 26 | // socket 27 | socketList []SocketConfig 28 | } 29 | 30 | func init() { 31 | var filepath string 32 | filepath = os.Getenv("SOCKET_MODULE_CONFIG") 33 | if filepath == "" { 34 | filepath = "/etc/kubeedge/config/socket_module.yaml" 35 | if _, err := os.Stat(filepath); err != nil { 36 | return 37 | } 38 | } 39 | 40 | buildinModuleConfig = InitBuildinModuleConfig(filepath) 41 | } 42 | 43 | var ( 44 | buildinModuleConfig *BuildinModuleConfig 45 | ) 46 | 47 | // InitBuildinModuleConfig init buildin module config 48 | func InitBuildinModuleConfig(filepath string) *BuildinModuleConfig { 49 | moduleConfig := BuildinModuleConfig{} 50 | data, err := os.ReadFile(filepath) 51 | if err != nil { 52 | klog.Errorf("failed to read file %v: %v", filepath, err) 53 | return nil 54 | } 55 | err = yaml.Unmarshal(data, &moduleConfig.socketList) 56 | if err != nil { 57 | klog.Errorf("failed to yaml unmarshal config: %v", err) 58 | return nil 59 | } 60 | 61 | return &moduleConfig 62 | } 63 | 64 | // GetClientSocketConfig get client socket config 65 | func GetClientSocketConfig(module string) (SocketConfig, error) { 66 | for _, socketConfig := range buildinModuleConfig.socketList { 67 | if socketConfig.ModuleName == module && !socketConfig.Server { 68 | return socketConfig, nil 69 | } 70 | } 71 | 72 | return SocketConfig{}, fmt.Errorf("failed to get socket config by name(%s)", module) 73 | } 74 | 75 | // GetServerSocketConfig get server socket config 76 | func GetServerSocketConfig() ([]SocketConfig, error) { 77 | var serversSocket []SocketConfig 78 | for _, socketConfig := range buildinModuleConfig.socketList { 79 | if socketConfig.Server { 80 | serversSocket = append(serversSocket, socketConfig) 81 | } 82 | } 83 | 84 | if len(serversSocket) != 0 { 85 | return serversSocket, nil 86 | } 87 | return []SocketConfig{}, fmt.Errorf("failed to get socket config") 88 | } 89 | -------------------------------------------------------------------------------- /pkg/core/socket/context_socket.go: -------------------------------------------------------------------------------- 1 | package socket 2 | 3 | import ( 4 | "crypto/tls" 5 | "encoding/json" 6 | "fmt" 7 | "sync" 8 | "time" 9 | 10 | "k8s.io/klog/v2" 11 | 12 | "github.com/kubeedge/beehive/pkg/common" 13 | "github.com/kubeedge/beehive/pkg/core/model" 14 | "github.com/kubeedge/beehive/pkg/core/socket/broker" 15 | "github.com/kubeedge/beehive/pkg/core/socket/config" 16 | "github.com/kubeedge/beehive/pkg/core/socket/store" 17 | "github.com/kubeedge/beehive/pkg/core/socket/wrapper" 18 | ) 19 | 20 | // Context is object for Context Socket 21 | type Context struct { 22 | // module -> context 23 | contexts map[string]*context 24 | // group -> context 25 | groups map[string]*context 26 | sync.RWMutex 27 | } 28 | 29 | var globalSocketContext Context 30 | var once = sync.Once{} 31 | 32 | func InitSocketContext() *Context { 33 | once.Do(func() { 34 | globalSocketContext.contexts = make(map[string]*context) 35 | globalSocketContext.groups = make(map[string]*context) 36 | }) 37 | return &globalSocketContext 38 | } 39 | 40 | func (s *Context) AddModule(info *common.ModuleInfo) { 41 | name := info.ModuleName 42 | s.setContext(name) 43 | context := s.getContext(name) 44 | if !info.IsRemote { 45 | context.AddModule(name, info.Connection) 46 | } else { 47 | context.AddModuleRemote(name) 48 | } 49 | } 50 | 51 | // AddModuleGroup add module group 52 | func (s *Context) AddModuleGroup(module, group string) { 53 | s.Lock() 54 | s.groups[module] = s.contexts[module] 55 | s.Unlock() 56 | 57 | s.getContext(module).AddModuleGroup(module, group) 58 | } 59 | 60 | // Cleanup cleanup 61 | func (s *Context) Cleanup(module string) { 62 | s.getContext(module).Cleanup(module) 63 | } 64 | 65 | // Send send 66 | func (s *Context) Send(module string, message model.Message) { 67 | s.getContext(module).Send(module, message) 68 | } 69 | 70 | // Receive receive 71 | func (s *Context) Receive(module string) (model.Message, error) { 72 | return s.getContext(module).Receive(module) 73 | } 74 | 75 | // SendSync send sync 76 | func (s *Context) SendSync(module string, message model.Message, timeout time.Duration) (model.Message, error) { 77 | return s.getContext(module).SendSync(module, message, timeout) 78 | } 79 | 80 | // SendResp send the response that got by NewRespByMessage 81 | func (s *Context) SendResp(message model.Message) { 82 | module := message.GetSource() 83 | s.getContext(module).SendResp(message) 84 | } 85 | 86 | // SendToGroup send to group 87 | func (s *Context) SendToGroup(group string, message model.Message) { 88 | s.getGroupContext(group).SendToGroup(group, message) 89 | } 90 | 91 | // SendToGroupSync send to group sync 92 | func (s *Context) SendToGroupSync(module string, message model.Message, timeout time.Duration) error { 93 | return s.getContext(module).SendToGroupSync(module, message, timeout) 94 | } 95 | 96 | func (s *Context) getGroupContext(group string) *context { 97 | s.RLock() 98 | defer s.RUnlock() 99 | return s.groups[group] 100 | } 101 | 102 | func (s *Context) getContext(module string) *context { 103 | s.RLock() 104 | defer s.RUnlock() 105 | return s.contexts[module] 106 | } 107 | 108 | func (s *Context) setContext(module string) { 109 | s.Lock() 110 | defer s.Unlock() 111 | s.contexts[module] = newContext(module) 112 | } 113 | 114 | // context module socket 115 | type context struct { 116 | name string 117 | address string 118 | moduleType string 119 | bufferSize int 120 | 121 | certificate tls.Certificate 122 | store *store.PipeStore 123 | broker *broker.RemoteBroker 124 | } 125 | 126 | // newContext new module socket 127 | func newContext(module string) *context { 128 | sConfig, err := config.GetClientSocketConfig(module) 129 | if err != nil { 130 | klog.Errorf("failed to get config with error %+v", err) 131 | return nil 132 | } 133 | 134 | certificate, err := getCert(&sConfig) 135 | if err != nil { 136 | klog.Errorf("failed to get cert with error %+v", err) 137 | } 138 | 139 | remoteBroker := broker.NewRemoteBroker() 140 | 141 | return &context{ 142 | name: sConfig.ModuleName, 143 | moduleType: sConfig.SocketType, 144 | address: sConfig.Address, 145 | bufferSize: sConfig.BufferSize, 146 | certificate: certificate, 147 | broker: remoteBroker, 148 | store: store.NewPipeStore(), 149 | } 150 | } 151 | 152 | // AddModuleRemote add module remote 153 | func (m *context) AddModuleRemote(module string) { 154 | klog.Infof("add remote module: %s", module) 155 | conn := m.Connect(module, GetConnectFunc(m.moduleType)) 156 | if conn == nil { 157 | // never come here !! 158 | klog.Errorf("failed to connect") 159 | } 160 | } 161 | 162 | // AddModule add module 163 | func (m *context) AddModule(module string, usConn interface{}) { 164 | klog.Infof("add module: %v", module) 165 | conn, ok := usConn.(wrapper.Conn) 166 | if !ok { 167 | klog.Errorf("failed to add module, bad us conn") 168 | return 169 | } 170 | m.store.Add(module, conn) 171 | } 172 | 173 | // AddModuleGroup add module group 174 | func (m *context) AddModuleGroup(module, group string) { 175 | klog.Infof("add module(%v) to group(%v)", module, group) 176 | pipeInfo, err := m.store.Get(module) 177 | if err != nil { 178 | klog.Warningf("bad module name %s", module) 179 | return 180 | } 181 | 182 | conn := pipeInfo.Wrapper() 183 | if conn != nil { 184 | m.store.AddGroup(module, group, conn) 185 | } 186 | } 187 | 188 | // Cleanup cleanup 189 | func (m *context) Cleanup(module string) { 190 | klog.Infof("clean up module: %s", module) 191 | pipeInfo, err := m.store.Get(module) 192 | if err != nil { 193 | return 194 | } 195 | 196 | conn := pipeInfo.Wrapper() 197 | if conn != nil { 198 | err = conn.Close() 199 | } 200 | m.store.Delete(module) 201 | } 202 | 203 | // Send send 204 | func (m *context) Send(module string, message model.Message) { 205 | pipeInfo, err := m.store.Get(module) 206 | if err != nil { 207 | klog.Warningf("failed to get module %s", module) 208 | return 209 | } 210 | message.SetType(m.moduleType) 211 | message.SetDestination(module) 212 | conn := pipeInfo.Wrapper() 213 | if conn != nil { 214 | err = m.broker.Send(conn, message) 215 | return 216 | } 217 | klog.Warningf("bad module name %s", module) 218 | } 219 | 220 | // Receive receive 221 | func (m *context) Receive(module string) (model.Message, error) { 222 | pipeInfo, err := m.store.Get(module) 223 | if err != nil { 224 | klog.Warningf("failed to get module pipe: %s", module) 225 | return model.Message{}, fmt.Errorf("failed to get module pipe: %v", err) 226 | } 227 | 228 | conn := pipeInfo.Wrapper() 229 | if conn != nil { 230 | return m.broker.Receive(conn) 231 | } 232 | 233 | klog.Warningf("bad module name: %s", module) 234 | return model.Message{}, fmt.Errorf("bad module name(%s)", module) 235 | } 236 | 237 | // SendSync send sync 238 | func (m *context) SendSync(module string, message model.Message, timeout time.Duration) (model.Message, error) { 239 | pipeInfo, err := m.store.Get(module) 240 | if err != nil { 241 | klog.Warningf("failed to get module pipe: %s", module) 242 | return model.Message{}, fmt.Errorf("failed to get module pipe: %v", err) 243 | } 244 | 245 | conn := pipeInfo.Wrapper() 246 | if conn == nil { 247 | klog.Warningf("bad module name: %s", module) 248 | return model.Message{}, fmt.Errorf("bad module name(%s)", module) 249 | } 250 | message.SetType(m.moduleType) 251 | message.SetDestination(module) 252 | return m.broker.SendSync(conn, message, timeout) 253 | } 254 | 255 | // SendResp send the response that got by NewRespByMessage 256 | func (m *context) SendResp(message model.Message) { 257 | pipeInfo, err := m.store.Get(message.GetSource()) 258 | if err != nil { 259 | klog.Warningf("failed to get module:%s", message.GetSource()) 260 | return 261 | } 262 | 263 | conn := pipeInfo.Wrapper() 264 | if conn == nil { 265 | klog.Warningf("bad module name:%s", message.GetSource()) 266 | return 267 | } 268 | message.SetDestination(message.GetSource()) 269 | err = m.broker.Send(conn, message) 270 | } 271 | 272 | // SendToGroup send to group 273 | func (m *context) SendToGroup(group string, message model.Message) { 274 | var err error 275 | walkFunc := func(module string, pipe store.PipeInfo) error { 276 | conn := pipe.Wrapper() 277 | if conn == nil { 278 | klog.Warningf("bad pipe") 279 | return nil 280 | } 281 | message.SetDestination(module) 282 | err = m.broker.Send(conn, message) 283 | if err != nil { 284 | return err 285 | } 286 | return nil 287 | } 288 | 289 | err = m.store.WalkGroup(group, walkFunc) 290 | } 291 | 292 | // SendToGroupSync send to group sync 293 | func (*context) SendToGroupSync(string, model.Message, time.Duration) error { 294 | return fmt.Errorf("not supported now") 295 | } 296 | 297 | // ModuleExchange module exchange 298 | type ModuleExchange struct { 299 | Modules []string `json:"modules"` 300 | Groups map[string][]string `json:"groups"` 301 | } 302 | 303 | func (m *context) exchangeModuleInfo(conn wrapper.Conn, module string) error { 304 | moduleMsg := model.NewMessage(""). 305 | BuildRouter(module, "", common.ResourceTypeModule, common.OperationTypeModule). 306 | SetType(m.moduleType). 307 | FillBody("") 308 | resp, err := m.broker.SendSyncInternal(conn, *moduleMsg, 0) 309 | if err != nil { 310 | klog.Errorf("failed to send module message with error %+v", err) 311 | return fmt.Errorf("failed to send module message, response:%+v, error: %+v", resp, err) 312 | } 313 | 314 | var exchange ModuleExchange 315 | bytes, err := json.Marshal(resp.GetContent()) 316 | if err != nil { 317 | klog.Errorf("failed to marshal response with error %+v", err) 318 | return fmt.Errorf("failed to marshal response, error: %+v", err) 319 | } 320 | 321 | err = json.Unmarshal(bytes, &exchange) 322 | if err != nil { 323 | klog.Errorf("bad modules info from remote with error %+v", err) 324 | return fmt.Errorf("bad modules info from remote %+v with error %s", resp, err.Error()) 325 | } 326 | 327 | // add modules into store 328 | // all the remote module use the conn from this side 329 | for _, name := range exchange.Modules { 330 | if name == module { 331 | continue 332 | } 333 | klog.Infof("socket module: %s", name) 334 | m.store.Add(name, conn) 335 | } 336 | 337 | // add group into store 338 | for group, modules := range exchange.Groups { 339 | for _, module := range modules { 340 | m.store.AddGroup(module, group, conn) 341 | } 342 | } 343 | 344 | klog.Infof("success to send module message") 345 | return nil 346 | } 347 | 348 | func (m *context) Connect(module string, connect broker.ConnectFunc) wrapper.Conn { 349 | opts := broker.ConnectOptions{ 350 | Address: m.address, 351 | MessageType: m.moduleType, 352 | BufferSize: m.bufferSize, 353 | Cert: m.certificate, 354 | } 355 | 356 | for { 357 | conn := m.broker.Connect(opts, connect) 358 | if conn == nil { 359 | time.Sleep(connectPeriod) 360 | continue 361 | } 362 | 363 | m.AddModule(module, conn) 364 | 365 | // send module message 366 | err := m.exchangeModuleInfo(conn, module) 367 | if err == nil { 368 | return conn 369 | } 370 | klog.Errorf("error to connect with %+v", err) 371 | 372 | // try to redial 373 | err = conn.Close() 374 | time.Sleep(connectPeriod) 375 | } 376 | } 377 | -------------------------------------------------------------------------------- /pkg/core/socket/helper.go: -------------------------------------------------------------------------------- 1 | package socket 2 | 3 | import ( 4 | "crypto/tls" 5 | "fmt" 6 | "net" 7 | "time" 8 | 9 | "k8s.io/klog/v2" 10 | 11 | "github.com/kubeedge/beehive/pkg/common" 12 | "github.com/kubeedge/beehive/pkg/core/socket/broker" 13 | "github.com/kubeedge/beehive/pkg/core/socket/config" 14 | ) 15 | 16 | const ( 17 | connectPeriod = 5 * time.Second 18 | 19 | // TODO: configurable 20 | HandshakeTimeout = 60 * time.Second 21 | ) 22 | 23 | func getCert(config *config.SocketConfig) (tls.Certificate, error) { 24 | if config.Key == "" && 25 | config.Cert == "" { 26 | return tls.Certificate{}, nil 27 | } 28 | 29 | var err error 30 | var certificate tls.Certificate 31 | if config.Cert != "" && config.Key != "" { 32 | certificate, err = tls.LoadX509KeyPair(config.Cert, config.Key) 33 | } else { 34 | err = fmt.Errorf("failed to get x509 key pair") 35 | } 36 | return certificate, err 37 | } 38 | 39 | // GetConnectFunc get connect func 40 | func GetConnectFunc(moduleType string) broker.ConnectFunc { 41 | switch moduleType { 42 | case common.MsgCtxTypeUS: 43 | return Connect 44 | } 45 | klog.Warningf("not supported module type: %v", moduleType) 46 | return nil 47 | } 48 | 49 | // Connect socket connect 50 | func Connect(opts broker.ConnectOptions) (interface{}, error) { 51 | conn, err := net.Dial(opts.MessageType, opts.Address) 52 | if err != nil { 53 | klog.Errorf("failed to dail addrs: %s", opts.Address) 54 | return nil, err 55 | } 56 | return conn, nil 57 | } 58 | -------------------------------------------------------------------------------- /pkg/core/socket/modules/socket/server.go: -------------------------------------------------------------------------------- 1 | package socket 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | "os" 7 | "strings" 8 | "time" 9 | 10 | "k8s.io/klog/v2" 11 | 12 | "github.com/kubeedge/beehive/pkg/common" 13 | "github.com/kubeedge/beehive/pkg/core" 14 | beehiveContext "github.com/kubeedge/beehive/pkg/core/context" 15 | "github.com/kubeedge/beehive/pkg/core/model" 16 | "github.com/kubeedge/beehive/pkg/core/socket/config" 17 | "github.com/kubeedge/beehive/pkg/core/socket/wrapper" 18 | ) 19 | 20 | const ( 21 | acceptPeriod = 5 * time.Second 22 | ) 23 | 24 | func init() { 25 | configList, err := config.GetServerSocketConfig() 26 | if err != nil { 27 | klog.Errorf("failed to get server socket config, error: %+v", err) 28 | return 29 | } 30 | 31 | for _, sConfig := range configList { 32 | core.Register(&Server{ 33 | enable: true, 34 | name: sConfig.ModuleName, 35 | address: sConfig.Address, 36 | buffSize: uint64(sConfig.BufferSize), 37 | socketType: sConfig.SocketType, 38 | connMax: sConfig.ConnNumberMax, 39 | pipeKeeper: make(chan struct{}, sConfig.ConnNumberMax), 40 | stopChan: make(chan struct{}), 41 | }) 42 | } 43 | } 44 | 45 | func (m *Server) serveSocket() error { 46 | if strings.Contains(m.socketType, "unix") { 47 | err := os.Remove(m.address) 48 | if err != nil { 49 | klog.Errorf("failed to remove address, error: %+v", err) 50 | } 51 | } 52 | 53 | listener, err := net.Listen(m.socketType, m.address) 54 | //defer listener.Close() 55 | if err != nil { 56 | klog.Errorf("failed to listen to socket, error: %+v", err) 57 | return fmt.Errorf("failed to listen to socket, error: %+v", err) 58 | } 59 | m.listener = listener 60 | klog.Infof("Listening on addr: %s", listener.Addr().String()) 61 | 62 | if _, err := os.Stat(m.address); err == nil { 63 | err = os.Chmod(m.address, 0600) 64 | if err != nil { 65 | klog.Errorf("Chmod failed: %s", m.address) 66 | return err 67 | } 68 | } 69 | 70 | for { 71 | conn, err := listener.Accept() 72 | klog.Infof("Connected from %s", conn.LocalAddr().String()) 73 | if err != nil { 74 | klog.Errorf("failed to accept with error %+v", err) 75 | return fmt.Errorf("failed to accept, error: %+v", err) 76 | } 77 | 78 | select { 79 | case m.pipeKeeper <- struct{}{}: 80 | go m.handleServerConn(conn) 81 | default: 82 | klog.Warningf("reject remote, because of connection exceed the max value: %d", m.connMax) 83 | err := conn.Close() 84 | if err != nil { 85 | klog.Errorf("conn closed with error %+v", err) 86 | } 87 | time.Sleep(acceptPeriod) 88 | } 89 | } 90 | } 91 | 92 | // HandleServerConn handler sever 93 | func (m *Server) handleServerConn(c net.Conn) { 94 | conn := wrapper.NewWrapper(m.socketType, c, int(m.buffSize)) 95 | 96 | // close connectinon 97 | // release pipe keeper 98 | defer func() { 99 | err := conn.Close() 100 | <-m.pipeKeeper 101 | if err != nil { 102 | return 103 | } 104 | }() 105 | 106 | for { 107 | err := m.handleServerMessage(conn) 108 | if err != nil { 109 | klog.Errorf("failed to handle server message with error %+v", err) 110 | return 111 | } 112 | } 113 | } 114 | 115 | func (m *Server) processModuleMessage(conn wrapper.Conn, message *model.Message) error { 116 | switch message.GetOperation() { 117 | case common.OperationTypeModule: 118 | if !IsModuleEnabled(message.GetSource()) { 119 | return fmt.Errorf("this module is not enabled, message: %s", message.String()) 120 | } 121 | moduleName := message.GetSource() 122 | moduleGroup := message.GetSource() 123 | 124 | add := &common.ModuleInfo{ 125 | ModuleName: moduleName, 126 | ModuleType: m.socketType, 127 | ModuleSocket: common.ModuleSocket{ 128 | IsRemote: false, 129 | Connection: conn, 130 | }, 131 | } 132 | 133 | beehiveContext.AddModule(add) 134 | beehiveContext.AddModuleGroup(moduleName, moduleGroup) 135 | resp := message.NewRespByMessage(message, core.GetModuleExchange()) 136 | beehiveContext.SendResp(*resp) 137 | } 138 | return nil 139 | } 140 | 141 | // HandleServerContext handler ctx 142 | func (m *Server) handleServerMessage(conn wrapper.Conn) error { 143 | var message model.Message 144 | err := conn.ReadJSON(&message) 145 | if err != nil { 146 | klog.Errorf("failed to read json with error %+v", err) 147 | return fmt.Errorf("failed to read json, error:%+v", err) 148 | } 149 | 150 | // log.LOGGER.Infof("receive message: %+v", message) 151 | switch message.GetResource() { 152 | case common.ResourceTypeModule: 153 | // remote module message 154 | return m.processModuleMessage(conn, &message) 155 | } 156 | 157 | if !IsModuleEnabled(message.GetSource()) { 158 | klog.Warning("the module is not enabled, just discard") 159 | return fmt.Errorf("the module is not enabled, just discard") 160 | } 161 | 162 | // transmit the message 163 | // log.LOGGER.Infof("server: %+v", message) 164 | beehiveContext.Send(message.GetDestination(), message) 165 | return nil 166 | } 167 | 168 | // StartServer start server 169 | func (m *Server) startServer() { 170 | err := m.serveSocket() 171 | if err != nil { 172 | klog.Errorf("failed to start server") 173 | } 174 | } 175 | 176 | // StopServer start server 177 | func (m *Server) stopServer() { 178 | err := m.listener.Close() 179 | if err != nil { 180 | klog.Errorf("failed to close server with error %+v", err) 181 | } 182 | } 183 | 184 | func IsModuleEnabled(m string) bool { 185 | _, err := config.GetClientSocketConfig(m) 186 | return err == nil 187 | } 188 | -------------------------------------------------------------------------------- /pkg/core/socket/modules/socket/socket.go: -------------------------------------------------------------------------------- 1 | package socket 2 | 3 | import ( 4 | "net" 5 | ) 6 | 7 | // Server module socket server 8 | type Server struct { 9 | enable bool 10 | name string 11 | address string 12 | buffSize uint64 13 | socketType string 14 | connMax int 15 | listener net.Listener 16 | pipeKeeper chan struct{} 17 | stopChan chan struct{} 18 | } 19 | 20 | // Name name 21 | func (m *Server) Name() string { 22 | return m.name 23 | } 24 | 25 | // Group group 26 | func (m *Server) Group() string { 27 | return m.name 28 | } 29 | 30 | // Start start 31 | func (m *Server) Start() { 32 | m.startServer() 33 | } 34 | 35 | // Enable enable 36 | func (m *Server) Enable() bool { 37 | return m.enable 38 | } 39 | -------------------------------------------------------------------------------- /pkg/core/socket/store/pipe.go: -------------------------------------------------------------------------------- 1 | package store 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | 7 | "k8s.io/klog/v2" 8 | ) 9 | 10 | // PipeStore pipe store 11 | type PipeStore struct { 12 | pipeMap map[string]PipeInfo 13 | pipeMapLock sync.RWMutex 14 | groupPipeMap map[string]map[string]PipeInfo 15 | groupPipeMapLock sync.RWMutex 16 | } 17 | 18 | // NewPipeStore new pipe store 19 | func NewPipeStore() *PipeStore { 20 | return &PipeStore{ 21 | pipeMap: make(map[string]PipeInfo), 22 | groupPipeMap: make(map[string]map[string]PipeInfo), 23 | } 24 | } 25 | 26 | // Add add 27 | func (s *PipeStore) Add(module string, pipe interface{}) { 28 | s.pipeMapLock.Lock() 29 | defer s.pipeMapLock.Unlock() 30 | s.pipeMap[module] = PipeInfo{pipe: pipe} 31 | } 32 | 33 | // Delete delete 34 | func (s *PipeStore) Delete(module string) { 35 | // delete module conn from conn map 36 | s.pipeMapLock.Lock() 37 | _, exist := s.pipeMap[module] 38 | if !exist { 39 | klog.Warningf("failed to get pipe, module: %s", module) 40 | return 41 | } 42 | delete(s.pipeMap, module) 43 | s.pipeMapLock.Unlock() 44 | 45 | // delete module conn from group conn map 46 | s.groupPipeMapLock.Lock() 47 | for _, moduleMap := range s.groupPipeMap { 48 | if _, exist := moduleMap[module]; exist { 49 | delete(moduleMap, module) 50 | break 51 | } 52 | } 53 | s.groupPipeMapLock.Unlock() 54 | } 55 | 56 | // Get get 57 | func (s *PipeStore) Get(module string) (PipeInfo, error) { 58 | s.pipeMapLock.RLock() 59 | defer s.pipeMapLock.RUnlock() 60 | 61 | if info, exist := s.pipeMap[module]; exist { 62 | return info, nil 63 | } 64 | return PipeInfo{}, fmt.Errorf("failed to get module(%s)", module) 65 | } 66 | 67 | // AddGroup add group 68 | func (s *PipeStore) AddGroup(module, group string, pipe interface{}) { 69 | s.groupPipeMapLock.Lock() 70 | defer s.groupPipeMapLock.Unlock() 71 | 72 | if _, exist := s.groupPipeMap[group]; !exist { 73 | s.groupPipeMap[group] = make(map[string]PipeInfo) 74 | } 75 | s.groupPipeMap[group][module] = PipeInfo{pipe: pipe} 76 | } 77 | 78 | // GetGroup get group 79 | func (s *PipeStore) GetGroup(group string) map[string]PipeInfo { 80 | s.groupPipeMapLock.RLock() 81 | defer s.groupPipeMapLock.RUnlock() 82 | 83 | if _, exist := s.groupPipeMap[group]; exist { 84 | return s.groupPipeMap[group] 85 | } 86 | klog.Warningf("failed to get group, type: %s", group) 87 | return nil 88 | } 89 | 90 | // WalkGroup walk group 91 | func (s *PipeStore) WalkGroup(group string, walkFunc func(string, PipeInfo) error) error { 92 | s.groupPipeMapLock.RLock() 93 | defer s.groupPipeMapLock.RUnlock() 94 | 95 | if _, exist := s.groupPipeMap[group]; !exist { 96 | klog.Warningf("failed to get group, type: %s", group) 97 | return fmt.Errorf("failed to get group, type(%s)", group) 98 | } 99 | 100 | for module, pipe := range s.groupPipeMap[group] { 101 | err := walkFunc(module, pipe) 102 | if err != nil { 103 | return err 104 | } 105 | } 106 | 107 | return nil 108 | } 109 | -------------------------------------------------------------------------------- /pkg/core/socket/store/pipeinfo.go: -------------------------------------------------------------------------------- 1 | package store 2 | 3 | import ( 4 | "net" 5 | 6 | "k8s.io/klog/v2" 7 | 8 | "github.com/kubeedge/beehive/pkg/core/model" 9 | "github.com/kubeedge/beehive/pkg/core/socket/wrapper" 10 | ) 11 | 12 | // PipeInfo pipe info 13 | type PipeInfo struct { 14 | pipe interface{} 15 | } 16 | 17 | // Channel channel 18 | func (info *PipeInfo) Channel() chan model.Message { 19 | if ch, ok := info.pipe.(chan model.Message); ok { 20 | return ch 21 | } 22 | klog.Warning("failed to get channel") 23 | return nil 24 | } 25 | 26 | // Socket socket 27 | func (info *PipeInfo) Socket() net.Conn { 28 | if socket, ok := info.pipe.(net.Conn); ok { 29 | return socket 30 | } 31 | klog.Warning("failed to get socket") 32 | return nil 33 | } 34 | 35 | // Wrapper wrapper 36 | func (info *PipeInfo) Wrapper() wrapper.Conn { 37 | if socket, ok := info.pipe.(wrapper.Conn); ok { 38 | return socket 39 | } 40 | klog.Warning("failed to get conn wrapper") 41 | return nil 42 | } 43 | -------------------------------------------------------------------------------- /pkg/core/socket/synckeeper/keeper.go: -------------------------------------------------------------------------------- 1 | package synckeeper 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | 7 | "k8s.io/klog/v2" 8 | 9 | "github.com/kubeedge/beehive/pkg/core/model" 10 | ) 11 | 12 | // Keeper keeper 13 | type Keeper struct { 14 | syncKeeper map[string]chan model.Message 15 | keeperLock sync.RWMutex 16 | } 17 | 18 | // NewKeeper new keeper 19 | func NewKeeper() *Keeper { 20 | return &Keeper{ 21 | syncKeeper: make(map[string]chan model.Message), 22 | } 23 | } 24 | 25 | // SendToKeepChannel send to keep channel 26 | func (k *Keeper) SendToKeepChannel(message model.Message) error { 27 | k.keeperLock.RLock() 28 | defer k.keeperLock.RUnlock() 29 | 30 | channel, exist := k.syncKeeper[message.GetParentID()] 31 | if !exist { 32 | klog.Errorf("failed to get sync keeper channel, message: %s", message.String()) 33 | return fmt.Errorf("failed to get sync keeper channel, message:%s", message.String()) 34 | } 35 | 36 | // send response into synckeep channel 37 | select { 38 | case channel <- message: 39 | default: 40 | klog.Errorf("failed to send message to sync keep channel") 41 | return fmt.Errorf("failed to send message to sync keep channel") 42 | } 43 | return nil 44 | } 45 | 46 | // AddKeepChannel add keep channel 47 | func (k *Keeper) AddKeepChannel(msgID string) chan model.Message { 48 | k.keeperLock.Lock() 49 | defer k.keeperLock.Unlock() 50 | tempChannel := make(chan model.Message) 51 | k.syncKeeper[msgID] = tempChannel 52 | return tempChannel 53 | } 54 | 55 | // DeleteKeepChannel delete keep channel 56 | func (k *Keeper) DeleteKeepChannel(msgID string) { 57 | k.keeperLock.Lock() 58 | defer k.keeperLock.Unlock() 59 | delete(k.syncKeeper, msgID) 60 | } 61 | 62 | // IsSyncResponse is sync response 63 | func (k *Keeper) IsSyncResponse(msgID string) bool { 64 | k.keeperLock.RLock() 65 | defer k.keeperLock.RUnlock() 66 | _, exist := k.syncKeeper[msgID] 67 | return exist 68 | } 69 | -------------------------------------------------------------------------------- /pkg/core/socket/synckeeper/keeper_test.go: -------------------------------------------------------------------------------- 1 | package synckeeper 2 | 3 | import ( 4 | "testing" 5 | "time" 6 | 7 | "github.com/kubeedge/beehive/pkg/core/model" 8 | ) 9 | 10 | // TestNewKeeper test new keeper 11 | func TestNewKeeper(t *testing.T) { 12 | keeper := NewKeeper() 13 | message := model.NewMessage("").SetRoute("source", "dest").FillBody("hello") 14 | ch := keeper.AddKeepChannel(message.GetID()) 15 | go func() { 16 | err := keeper.SendToKeepChannel(*message.NewRespByMessage(message, "response")) 17 | if err != nil { 18 | t.Errorf("failed to send to keeper") 19 | return 20 | } 21 | }() 22 | 23 | select { 24 | case msg := <-ch: 25 | if !keeper.IsSyncResponse(msg.GetParentID()) { 26 | t.Fatalf("bad parent id") 27 | } 28 | case <-time.After(time.Second): 29 | t.Fatalf("time out") 30 | } 31 | keeper.DeleteKeepChannel(message.GetID()) 32 | } 33 | -------------------------------------------------------------------------------- /pkg/core/socket/wrapper/packer/packer.go: -------------------------------------------------------------------------------- 1 | package packer 2 | 3 | import ( 4 | "bytes" 5 | "encoding/binary" 6 | "io" 7 | 8 | "k8s.io/klog/v2" 9 | ) 10 | 11 | const ( 12 | magicSize = 4 13 | versionSize = 2 14 | reservedSize = 2 15 | 16 | // MessageLenOffest message len offest 17 | MessageLenOffest = magicSize + versionSize + reservedSize 18 | // MessageOffset message offset 19 | MessageOffset = MessageLenOffest + 4 20 | // HeaderLen header len 21 | HeaderLen = MessageOffset 22 | ) 23 | 24 | var ( 25 | headerTags = [HeaderLen]byte{'b', 'e', 'e', 'h', 'v', '1', 'r', 'v', 0, 0, 0, 0} 26 | ) 27 | 28 | // Packer packer 29 | type Packer struct { 30 | Magic [magicSize]byte 31 | Version [versionSize]byte 32 | Reserved [reservedSize]byte 33 | Length int32 34 | Message []byte 35 | } 36 | 37 | // NewPacker new packer 38 | func NewPacker() *Packer { 39 | return &Packer{ 40 | Magic: [magicSize]byte{'b', 'e', 'e', 'h'}, 41 | Version: [versionSize]byte{'v', '1'}, 42 | Reserved: [reservedSize]byte{'r', 'v'}, 43 | } 44 | } 45 | 46 | // Validate validate 47 | func (p *Packer) Validate(data []byte) bool { 48 | if len(data) <= HeaderLen { 49 | return false 50 | } 51 | if !bytes.Equal(data[:magicSize], p.Magic[:magicSize]) { 52 | return false 53 | } 54 | if !bytes.Equal(data[magicSize:magicSize+versionSize], p.Version[:versionSize]) { 55 | return false 56 | } 57 | return true 58 | } 59 | 60 | // Write write 61 | func (p *Packer) Write(writer io.Writer) error { 62 | // fill message len 63 | headerTags[MessageLenOffest] = byte(uint32(p.Length) >> 24) 64 | headerTags[MessageLenOffest+1] = byte(uint32(p.Length) >> 16) 65 | headerTags[MessageLenOffest+2] = byte(uint32(p.Length) >> 8) 66 | headerTags[MessageLenOffest+3] = byte(uint32(p.Length)) 67 | err := binary.Write(writer, binary.BigEndian, &headerTags) 68 | if err != nil { 69 | return err 70 | } 71 | err = binary.Write(writer, binary.BigEndian, &p.Message) 72 | if err != nil { 73 | return err 74 | } 75 | return nil 76 | } 77 | 78 | // Read read 79 | func (p *Packer) Read(reader io.Reader) error { 80 | err := binary.Read(reader, binary.BigEndian, &p.Magic) 81 | if err != nil { 82 | return err 83 | } 84 | err = binary.Read(reader, binary.BigEndian, &p.Version) 85 | if err != nil { 86 | return err 87 | } 88 | err = binary.Read(reader, binary.BigEndian, &p.Reserved) 89 | if err != nil { 90 | return err 91 | } 92 | err = binary.Read(reader, binary.BigEndian, &p.Length) 93 | if err != nil { 94 | return err 95 | } 96 | err = binary.Read(reader, binary.BigEndian, &p.Message) 97 | if err != nil { 98 | return err 99 | } 100 | return err 101 | } 102 | 103 | // GetMessageLen get message len 104 | func (p *Packer) GetMessageLen(data []byte) int32 { 105 | length := int32(0) 106 | if len(data) < MessageOffset { 107 | return length 108 | } 109 | err := binary.Read(bytes.NewReader(data[MessageLenOffest:MessageOffset]), binary.BigEndian, &length) 110 | if err != nil { 111 | klog.Errorf("binary Read err %+v", err) 112 | } 113 | return length 114 | } 115 | -------------------------------------------------------------------------------- /pkg/core/socket/wrapper/reader/package.go: -------------------------------------------------------------------------------- 1 | package reader 2 | 3 | import ( 4 | "bufio" 5 | "bytes" 6 | "encoding/json" 7 | "fmt" 8 | "net" 9 | "sync" 10 | 11 | "k8s.io/klog/v2" 12 | 13 | "github.com/kubeedge/beehive/pkg/core/socket/wrapper/packer" 14 | ) 15 | 16 | // PackageReader package reader 17 | type PackageReader struct { 18 | scanner *bufio.Scanner 19 | lock sync.Mutex 20 | packer *packer.Packer 21 | } 22 | 23 | // NewPackageReader new package reader 24 | func NewPackageReader(obj interface{}, buffSize int) *PackageReader { 25 | if buffSize <= 0 { 26 | klog.Errorf("bad buffer size %d", buffSize) 27 | return nil 28 | } 29 | 30 | conn, ok := obj.(net.Conn) 31 | if !ok { 32 | klog.Errorf("bad conn obj") 33 | return nil 34 | } 35 | 36 | p := packer.NewPacker() 37 | splitFunc := func(data []byte, eof bool) (advance int, token []byte, err error) { 38 | if !eof && p.Validate(data) { 39 | length := p.GetMessageLen(data) 40 | packageLen := int(length) + packer.MessageOffset 41 | if packageLen <= len(data) { 42 | return packageLen, data[packer.MessageOffset:packageLen], nil 43 | } 44 | } 45 | return 46 | } 47 | scanner := bufio.NewScanner(conn) 48 | scanner.Split(splitFunc) 49 | scanner.Buffer(make([]byte, buffSize), buffSize) 50 | 51 | return &PackageReader{ 52 | scanner: scanner, 53 | packer: p, 54 | } 55 | } 56 | 57 | // Read read 58 | func (reader *PackageReader) Read() ([]byte, error) { 59 | reader.lock.Lock() 60 | defer reader.lock.Unlock() 61 | 62 | gotPackage := reader.scanner.Scan() 63 | if !gotPackage || reader.scanner.Err() != nil { 64 | return nil, fmt.Errorf("failed to scann package, error: %+v", reader.scanner.Err()) 65 | } 66 | return reader.scanner.Bytes(), nil 67 | } 68 | 69 | // ReadJSON read json 70 | func (reader *PackageReader) ReadJSON(obj interface{}) error { 71 | reader.lock.Lock() 72 | defer reader.lock.Unlock() 73 | 74 | gotPackage := reader.scanner.Scan() 75 | if !gotPackage || reader.scanner.Err() != nil { 76 | return fmt.Errorf("failed to scann package, error: %+v", reader.scanner.Err()) 77 | } 78 | return json.NewDecoder(bytes.NewReader(reader.scanner.Bytes())).Decode(obj) 79 | } 80 | -------------------------------------------------------------------------------- /pkg/core/socket/wrapper/reader/raw.go: -------------------------------------------------------------------------------- 1 | package reader 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "net" 7 | "sync" 8 | 9 | "k8s.io/klog/v2" 10 | ) 11 | 12 | // RawReader raw reader 13 | type RawReader struct { 14 | conn net.Conn 15 | lock sync.Mutex 16 | buffer []byte 17 | buffSize int 18 | } 19 | 20 | // NewRawReader new raw reader 21 | func NewRawReader(conn interface{}, buffSize int) *RawReader { 22 | if conn, ok := conn.(net.Conn); ok { 23 | return &RawReader{ 24 | conn: conn, 25 | buffSize: buffSize, 26 | } 27 | } 28 | klog.Warning("bad conn interface") 29 | return nil 30 | } 31 | 32 | // Read read 33 | func (r *RawReader) Read() ([]byte, error) { 34 | r.lock.Lock() 35 | defer r.lock.Unlock() 36 | 37 | if r.buffer == nil { 38 | r.buffer = make([]byte, r.buffSize) 39 | } 40 | 41 | nr, err := r.conn.Read(r.buffer) 42 | if err != nil { 43 | klog.Errorf("failed to read, error %+v", err) 44 | return nil, fmt.Errorf("failed to read, error: %+v", err) 45 | } 46 | return r.buffer[:nr], nil 47 | } 48 | 49 | // ReadJSON read json 50 | func (r *RawReader) ReadJSON(obj interface{}) error { 51 | //r.lock.Lock() 52 | //defer r.lock.Unlock() 53 | //return json.NewDecoder(r.conn).Decode(obj) 54 | buf, err := r.Read() 55 | if err != nil { 56 | return err 57 | } 58 | err = json.Unmarshal(buf, obj) 59 | if err != nil { 60 | klog.Errorf("failed to unmarshal message, context: %s, errpr: %+v", string(buf), err) 61 | return fmt.Errorf("failed to unmarshal message, error:%+v, context: %+v", err, string(buf)) 62 | } 63 | return nil 64 | } 65 | -------------------------------------------------------------------------------- /pkg/core/socket/wrapper/reader/reader.go: -------------------------------------------------------------------------------- 1 | package reader 2 | 3 | import ( 4 | "k8s.io/klog/v2" 5 | ) 6 | 7 | const ( 8 | // ReaderTypeRaw reader type raw 9 | ReaderTypeRaw = "raw" 10 | // ReaderTypePackage reader type package 11 | ReaderTypePackage = "package" 12 | ) 13 | 14 | // Reader reader 15 | type Reader interface { 16 | Read() ([]byte, error) 17 | ReadJSON(obj interface{}) error 18 | } 19 | 20 | // NewReader new reader 21 | func NewReader(readerType string, conn interface{}, buffSize int) Reader { 22 | switch readerType { 23 | case ReaderTypeRaw: 24 | return NewRawReader(conn, buffSize) 25 | case ReaderTypePackage: 26 | return NewPackageReader(conn, buffSize) 27 | } 28 | klog.Errorf("bad reader type: %s", readerType) 29 | return nil 30 | } 31 | -------------------------------------------------------------------------------- /pkg/core/socket/wrapper/wrapper.go: -------------------------------------------------------------------------------- 1 | package wrapper 2 | 3 | import ( 4 | "net" 5 | "time" 6 | 7 | "k8s.io/klog/v2" 8 | 9 | "github.com/kubeedge/beehive/pkg/core/socket/wrapper/reader" 10 | "github.com/kubeedge/beehive/pkg/core/socket/wrapper/writer" 11 | ) 12 | 13 | // Conn conn 14 | type Conn interface { 15 | Read() ([]byte, error) 16 | Write(message []byte) error 17 | 18 | ReadJSON(obj interface{}) error 19 | WriteJSON(obj interface{}) error 20 | 21 | Close() error 22 | 23 | SetReadDeadline(t time.Time) error 24 | } 25 | 26 | // ConnWrapper conn wrapper 27 | type ConnWrapper struct { 28 | conn interface{} 29 | reader reader.Reader 30 | writer writer.Writer 31 | } 32 | 33 | // NewWrapper new wrapper 34 | func NewWrapper(_ string, conn interface{}, buffSize int) Conn { 35 | readerType := reader.ReaderTypeRaw 36 | writerType := writer.WriterTypeRaw 37 | 38 | return &ConnWrapper{ 39 | conn: conn, 40 | reader: reader.NewReader(readerType, conn, buffSize), 41 | writer: writer.NewWriter(writerType, conn), 42 | } 43 | } 44 | 45 | // Read read 46 | func (w *ConnWrapper) Read() ([]byte, error) { 47 | return w.reader.Read() 48 | } 49 | 50 | // Write write 51 | func (w *ConnWrapper) Write(message []byte) error { 52 | return w.writer.Write(message) 53 | } 54 | 55 | // ReadJSON read json 56 | func (w *ConnWrapper) ReadJSON(obj interface{}) error { 57 | return w.reader.ReadJSON(obj) 58 | } 59 | 60 | // WriteJSON write json 61 | func (w *ConnWrapper) WriteJSON(obj interface{}) error { 62 | return w.writer.WriteJSON(obj) 63 | } 64 | 65 | // SetReadDeadline set read deadline 66 | func (w *ConnWrapper) SetReadDeadline(t time.Time) error { 67 | // TODO: put int Deadline 68 | var err error 69 | switch w.conn.(type) { 70 | case net.Conn: 71 | conn := w.conn.(net.Conn) 72 | err = conn.SetReadDeadline(t) 73 | default: 74 | klog.Warningf("unsupported conn type: %T", w.conn) 75 | } 76 | return err 77 | } 78 | 79 | // Close close 80 | func (w *ConnWrapper) Close() error { 81 | // TODO: put into Closer 82 | var err error 83 | switch w.conn.(type) { 84 | case net.Conn: 85 | conn := w.conn.(net.Conn) 86 | err = conn.Close() 87 | default: 88 | klog.Warningf("unsupported conn type: %T", w.conn) 89 | } 90 | return err 91 | } 92 | -------------------------------------------------------------------------------- /pkg/core/socket/wrapper/writer/package.go: -------------------------------------------------------------------------------- 1 | package writer 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "net" 7 | "sync" 8 | 9 | "k8s.io/klog/v2" 10 | 11 | "github.com/kubeedge/beehive/pkg/core/socket/wrapper/packer" 12 | ) 13 | 14 | // PackageWriter package writer 15 | type PackageWriter struct { 16 | packer *packer.Packer 17 | conn net.Conn 18 | lock sync.Mutex 19 | } 20 | 21 | // NewPackageWriter new package writer 22 | func NewPackageWriter(obj interface{}) *PackageWriter { 23 | if conn, ok := obj.(net.Conn); ok { 24 | packer := packer.NewPacker() 25 | return &PackageWriter{ 26 | conn: conn, 27 | packer: packer, 28 | } 29 | } 30 | klog.Errorf("bad conn obj") 31 | return nil 32 | } 33 | 34 | // Write write 35 | func (w *PackageWriter) Write(message []byte) error { 36 | w.lock.Lock() 37 | defer w.lock.Unlock() 38 | 39 | w.packer.Message = message 40 | w.packer.Length = int32(len(message)) 41 | err := w.packer.Write(w.conn) 42 | if err != nil { 43 | klog.Errorf("failed to packer with error %+v", err) 44 | return fmt.Errorf("failed to packer, error:%+v", err) 45 | } 46 | return nil 47 | } 48 | 49 | // WriteJSON write json 50 | func (w *PackageWriter) WriteJSON(obj interface{}) error { 51 | w.lock.Lock() 52 | defer w.lock.Unlock() 53 | 54 | objBytes, err := json.Marshal(obj) 55 | if err != nil { 56 | klog.Errorf("failed to marshal obj, error:%+v", err) 57 | return err 58 | } 59 | w.packer.Message = objBytes 60 | w.packer.Length = int32(len(objBytes)) 61 | err = w.packer.Write(w.conn) 62 | if err != nil { 63 | klog.Errorf("failed to packer, error:%+v", err) 64 | return fmt.Errorf("failed to packer, error:%+v", err) 65 | } 66 | return nil 67 | } 68 | -------------------------------------------------------------------------------- /pkg/core/socket/wrapper/writer/raw.go: -------------------------------------------------------------------------------- 1 | package writer 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "net" 7 | "sync" 8 | 9 | "k8s.io/klog/v2" 10 | ) 11 | 12 | // RawWriter raw writer 13 | type RawWriter struct { 14 | conn net.Conn 15 | lock sync.Mutex 16 | } 17 | 18 | // NewRawWriter new raw writer 19 | func NewRawWriter(obj interface{}) *RawWriter { 20 | if conn, ok := obj.(net.Conn); ok { 21 | return &RawWriter{conn: conn} 22 | } 23 | klog.Errorf("bad conn ") 24 | return nil 25 | } 26 | 27 | // Write write 28 | func (w *RawWriter) Write(message []byte) error { 29 | w.lock.Lock() 30 | defer w.lock.Unlock() 31 | 32 | number, err := w.conn.Write(message) 33 | if err != nil || number != len(message) { 34 | klog.Errorf("failed to write, error:%+v", err) 35 | return fmt.Errorf("failed to write, error: %+v", err) 36 | } 37 | return nil 38 | } 39 | 40 | // WriteJSON write json 41 | func (w *RawWriter) WriteJSON(obj interface{}) error { 42 | w.lock.Lock() 43 | defer w.lock.Unlock() 44 | return json.NewEncoder(w.conn).Encode(obj) 45 | } 46 | -------------------------------------------------------------------------------- /pkg/core/socket/wrapper/writer/writer.go: -------------------------------------------------------------------------------- 1 | package writer 2 | 3 | import ( 4 | "k8s.io/klog/v2" 5 | ) 6 | 7 | const ( 8 | // WriterTypeRaw writer type raw 9 | WriterTypeRaw = "raw" 10 | // WriterTypePackage writer type package 11 | WriterTypePackage = "package" 12 | ) 13 | 14 | // Writer writer 15 | type Writer interface { 16 | Write(message []byte) error 17 | WriteJSON(obj interface{}) error 18 | } 19 | 20 | // NewWriter new writer 21 | func NewWriter(writerType string, conn interface{}) Writer { 22 | switch writerType { 23 | case WriterTypeRaw: 24 | return NewRawWriter(conn) 25 | case WriterTypePackage: 26 | return NewPackageWriter(conn) 27 | } 28 | klog.Errorf("bad writer type:%s", writerType) 29 | return nil 30 | } 31 | -------------------------------------------------------------------------------- /test/cmd/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/kubeedge/beehive/pkg/core" 5 | _ "github.com/kubeedge/beehive/test/modules" 6 | ) 7 | 8 | func main() { 9 | core.Run() 10 | } 11 | -------------------------------------------------------------------------------- /test/modules/test_destination.go: -------------------------------------------------------------------------------- 1 | package modules 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/kubeedge/beehive/pkg/core" 7 | beehiveContext "github.com/kubeedge/beehive/pkg/core/context" 8 | ) 9 | 10 | //Constants for module name and group 11 | const ( 12 | DestinationModule = "destinationmodule" 13 | DestinationGroup = "destinationgroup" 14 | ) 15 | 16 | type testModuleDest struct { 17 | } 18 | 19 | func (m *testModuleDest) Enable() bool { 20 | return true 21 | } 22 | 23 | func init() { 24 | core.Register(&testModuleDest{}) 25 | } 26 | 27 | func (*testModuleDest) Name() string { 28 | return DestinationModule 29 | } 30 | 31 | func (*testModuleDest) Group() string { 32 | return DestinationGroup 33 | } 34 | 35 | func (m *testModuleDest) Start() { 36 | message, err := beehiveContext.Receive(DestinationModule) 37 | fmt.Printf("destination module receive message:%v error:%v\n", message, err) 38 | message, err = beehiveContext.Receive(DestinationModule) 39 | fmt.Printf("destination module receive message:%v error:%v\n", message, err) 40 | resp := message.NewRespByMessage(&message, "fine") 41 | if message.IsSync() { 42 | beehiveContext.SendResp(*resp) 43 | } 44 | 45 | message, err = beehiveContext.Receive(DestinationModule) 46 | fmt.Printf("destination module receive message:%v error:%v\n", message, err) 47 | if message.IsSync() { 48 | resp = message.NewRespByMessage(&message, "fine") 49 | beehiveContext.SendResp(*resp) 50 | } 51 | 52 | //message, err = c.Receive(DestinationModule) 53 | //fmt.Printf("destination module receive message:%v error:%v\n", message, err) 54 | //if message.IsSync() { 55 | // resp = message.NewRespByMessage(&message, "20 years old") 56 | // c.SendResp(*resp) 57 | //} 58 | } 59 | -------------------------------------------------------------------------------- /test/modules/test_destination_group.go: -------------------------------------------------------------------------------- 1 | package modules 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/kubeedge/beehive/pkg/core" 7 | beehiveContext "github.com/kubeedge/beehive/pkg/core/context" 8 | ) 9 | 10 | //Constant for test module destination group name 11 | const ( 12 | DestinationGroupModule = "destinationgroupmodule" 13 | ) 14 | 15 | type testModuleDestGroup struct { 16 | } 17 | 18 | func (m *testModuleDestGroup) Enable() bool { 19 | return true 20 | } 21 | 22 | func init() { 23 | core.Register(&testModuleDestGroup{}) 24 | } 25 | 26 | func (*testModuleDestGroup) Name() string { 27 | return DestinationGroupModule 28 | } 29 | 30 | func (*testModuleDestGroup) Group() string { 31 | return DestinationGroup 32 | } 33 | 34 | func (m *testModuleDestGroup) Start() { 35 | message, err := beehiveContext.Receive(DestinationGroupModule) 36 | fmt.Printf("destination group module receive message:%v error:%v\n", message, err) 37 | if message.IsSync() { 38 | resp := message.NewRespByMessage(&message, "10 years old") 39 | beehiveContext.SendResp(*resp) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /test/modules/test_source.go: -------------------------------------------------------------------------------- 1 | package modules 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | 7 | "github.com/kubeedge/beehive/pkg/core" 8 | beehiveContext "github.com/kubeedge/beehive/pkg/core/context" 9 | "github.com/kubeedge/beehive/pkg/core/model" 10 | ) 11 | 12 | //Constants for module source and group 13 | const ( 14 | SourceModule = "sourcemodule" 15 | SourceGroup = "sourcegroup" 16 | ) 17 | 18 | type testModuleSource struct { 19 | } 20 | 21 | func init() { 22 | core.Register(&testModuleSource{}) 23 | } 24 | 25 | func (m *testModuleSource) Enable() bool { 26 | return true 27 | } 28 | 29 | func (*testModuleSource) Name() string { 30 | return SourceModule 31 | } 32 | 33 | func (*testModuleSource) Group() string { 34 | return SourceGroup 35 | } 36 | 37 | func (m *testModuleSource) Start() { 38 | message := model.NewMessage("").SetRoute(SourceModule, ""). 39 | SetResourceOperation("test", model.InsertOperation).FillBody("hello") 40 | beehiveContext.Send(DestinationModule, *message) 41 | 42 | message = model.NewMessage("").SetRoute(SourceModule, ""). 43 | SetResourceOperation("test", model.UpdateOperation).FillBody("how are you") 44 | resp, err := beehiveContext.SendSync(DestinationModule, *message, 5*time.Second) 45 | if err != nil { 46 | fmt.Printf("failed to send sync message, error:%v\n", err) 47 | } else { 48 | fmt.Printf("get resp: %v\n", resp) 49 | } 50 | 51 | message = model.NewMessage("").SetRoute(SourceModule, DestinationGroup). 52 | SetResourceOperation("test", model.DeleteOperation).FillBody("fine") 53 | beehiveContext.SendToGroup(DestinationGroup, *message) 54 | } 55 | --------------------------------------------------------------------------------