You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@eventmesh.apache.org by ch...@apache.org on 2023/05/23 02:43:29 UTC
[eventmesh-go] branch main updated (828be3d -> d471d4f)
This is an automated email from the ASF dual-hosted git repository.
chenguangsheng pushed a change to branch main
in repository https://gitbox.apache.org/repos/asf/eventmesh-go.git
from 828be3d Merge pull request #12 from Alonexc/Alonexc-patch-1
new 29729a9 Merge pull request #2 from apache/main
new 3deba4e add issue template
new d471d4f Merge pull request #16 from Alonexc/add_issue_template
The 26 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
Summary of changes:
.github/ISSUE_TEMPLATE/bug_report.yml | 92 ++++++++++++++++++++++++
.github/ISSUE_TEMPLATE/documentation_related.yml | 60 ++++++++++++++++
.github/ISSUE_TEMPLATE/enhancement_request.yml | 69 ++++++++++++++++++
.github/ISSUE_TEMPLATE/feature_request.yml | 61 ++++++++++++++++
.github/ISSUE_TEMPLATE/question.yml | 51 +++++++++++++
.github/ISSUE_TEMPLATE/unit_test.yml | 75 +++++++++++++++++++
6 files changed, 408 insertions(+)
create mode 100644 .github/ISSUE_TEMPLATE/bug_report.yml
create mode 100644 .github/ISSUE_TEMPLATE/documentation_related.yml
create mode 100644 .github/ISSUE_TEMPLATE/enhancement_request.yml
create mode 100644 .github/ISSUE_TEMPLATE/feature_request.yml
create mode 100644 .github/ISSUE_TEMPLATE/question.yml
create mode 100644 .github/ISSUE_TEMPLATE/unit_test.yml
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@eventmesh.apache.org
For additional commands, e-mail: commits-help@eventmesh.apache.org
[eventmesh-go] 15/26: Merge pull request #6 from nikam14/patch-1
Posted by ch...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
chenguangsheng pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/eventmesh-go.git
commit d6a3a973d87994cc778eb76e295e0486afa2449f
Merge: be275b7 dcefe50
Author: walleliu <li...@163.com>
AuthorDate: Sun Apr 23 18:53:11 2023 -0700
Merge pull request #6 from nikam14/patch-1
[ISSUE #377][Enhancement]remove invalid code
plugin/connector/standalone/standalone_connector_test.go | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@eventmesh.apache.org
For additional commands, e-mail: commits-help@eventmesh.apache.org
[eventmesh-go] 08/26: Removed invalid code.[producer_test.go] #3774
Posted by ch...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
chenguangsheng pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/eventmesh-go.git
commit 19fb439312cb931336f11d69e5e739398d6bc7d0
Author: Piyush <11...@users.noreply.github.com>
AuthorDate: Thu Apr 20 18:30:35 2023 +0530
Removed invalid code.[producer_test.go] #3774
---
plugin/connector/rocketmq/producer_test.go | 25 -------------------------
1 file changed, 25 deletions(-)
diff --git a/plugin/connector/rocketmq/producer_test.go b/plugin/connector/rocketmq/producer_test.go
index 576fe41..6ea0299 100644
--- a/plugin/connector/rocketmq/producer_test.go
+++ b/plugin/connector/rocketmq/producer_test.go
@@ -187,28 +187,3 @@ func GetFullEvent() *ce.Event {
return &event
}
-//func TestProducer_Publish_Broker(t *testing.T) {
-// properties := make(map[string]string)
-// properties["access_points"] = "127.0.0.1:9876"
-// producer := NewProducer()
-// producer.InitProducer(properties)
-// producer.Start()
-//
-// var wg sync.WaitGroup
-// wg.Add(1)
-// callback := &connector.SendCallback{
-// OnSuccess: func(result *connector.SendResult) {
-// require.True(t, result.Topic == "TopicTest")
-// wg.Done()
-// },
-// OnError: func(result *connector.ErrorResult) {
-// panic(result.Err)
-// },
-// }
-//
-// err := producer.Publish(context.Background(), GetFullEvent(), callback)
-// require.True(t, err == nil)
-//
-// wg.Wait()
-// producer.Shutdown()
-//}
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@eventmesh.apache.org
For additional commands, e-mail: commits-help@eventmesh.apache.org
[eventmesh-go] 20/26: Delete .github/workflows directory
Posted by ch...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
chenguangsheng pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/eventmesh-go.git
commit 811379f056a75c6ba1ced7eaaa1401bf28a963e2
Author: Alonexc <91...@users.noreply.github.com>
AuthorDate: Mon Apr 24 15:20:29 2023 +0800
Delete .github/workflows directory
---
.github/workflows/golangCI.yml | 24 ------------------------
1 file changed, 24 deletions(-)
diff --git a/.github/workflows/golangCI.yml b/.github/workflows/golangCI.yml
deleted file mode 100644
index c84bf79..0000000
--- a/.github/workflows/golangCI.yml
+++ /dev/null
@@ -1,24 +0,0 @@
-name: golangci-lint
-on:
- push:
- branches:
- - master
- pull_request:
- branches:
- - master
-
-jobs:
- golangci:
- name: lint
- runs-on: ubuntu-latest
- steps:
- - uses: actions/setup-go@v4
- with:
- go-version: '1.17'
- cache: false
- - uses: actions/checkout@v3
- - name: golangci-lint
- uses: golangci/golangci-lint-action@v3
- with:
- # Optional: version of golangci-lint to use in form of v1.2 or v1.2.3 or `latest` to use the latest version
- version: latest
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@eventmesh.apache.org
For additional commands, e-mail: commits-help@eventmesh.apache.org
[eventmesh-go] 16/26: Merge pull request #5 from piyush280599/patch-3
Posted by ch...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
chenguangsheng pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/eventmesh-go.git
commit d9b782b89dbb668ab5f0d68e4de4f7d16ce5f5c6
Merge: d6a3a97 d6efef2
Author: walleliu <li...@163.com>
AuthorDate: Sun Apr 23 18:54:13 2023 -0700
Merge pull request #5 from piyush280599/patch-3
[ISSUE #3773]Removed invalid code.[consumer_test.go]
plugin/connector/rocketmq/consumer_test.go | 25 -------------------------
1 file changed, 25 deletions(-)
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@eventmesh.apache.org
For additional commands, e-mail: commits-help@eventmesh.apache.org
[eventmesh-go] 09/26: Removed invalid code.[consumer_test.go] #3773
Posted by ch...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
chenguangsheng pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/eventmesh-go.git
commit d6efef25e6af65e124e5508ac9619351e34de11f
Author: Piyush <11...@users.noreply.github.com>
AuthorDate: Thu Apr 20 18:32:31 2023 +0530
Removed invalid code.[consumer_test.go] #3773
---
plugin/connector/rocketmq/consumer_test.go | 25 -------------------------
1 file changed, 25 deletions(-)
diff --git a/plugin/connector/rocketmq/consumer_test.go b/plugin/connector/rocketmq/consumer_test.go
index a801cca..1590878 100644
--- a/plugin/connector/rocketmq/consumer_test.go
+++ b/plugin/connector/rocketmq/consumer_test.go
@@ -93,31 +93,6 @@ func TestConsumer_Subscribe(t *testing.T) {
consumer.Shutdown()
}
-//func TestConsumer_Subscribe_Broker(t *testing.T) {
-// properties := make(map[string]string)
-// properties["access_points"] = "127.0.0.1:9876"
-// properties["consumer_group"] = "test_group"
-// consumer := NewConsumer()
-// consumer.InitConsumer(properties)
-// consumer.Start()
-//
-// listener := &connector.EventListener{
-// Consume: func(event *ce.Event, commitFunc connector.CommitFunc) error {
-// fmt.Println(event)
-// commitFunc(connector.CommitMessage)
-// return nil
-// },
-// }
-//
-// consumer.RegisterEventListener(listener)
-// err := consumer.Subscribe("TopicTest")
-// if err != nil {
-// panic(err)
-// }
-//
-// time.Sleep(100 * time.Second)
-//}
-
func GetFullMessage() *primitive.Message {
event := ce.NewEvent()
event.SetSubject("TopicTest")
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@eventmesh.apache.org
For additional commands, e-mail: commits-help@eventmesh.apache.org
[eventmesh-go] 17/26: Merge pull request #4 from piyush280599/patch-2
Posted by ch...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
chenguangsheng pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/eventmesh-go.git
commit 9dabd4b9fcb5d0b4441a762d4e6a5868e2beecdb
Merge: d9b782b 19fb439
Author: walleliu <li...@163.com>
AuthorDate: Sun Apr 23 18:54:39 2023 -0700
Merge pull request #4 from piyush280599/patch-2
[ISSUE #3774]Removed invalid code.[producer_test.go]
plugin/connector/rocketmq/producer_test.go | 25 -------------------------
1 file changed, 25 deletions(-)
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@eventmesh.apache.org
For additional commands, e-mail: commits-help@eventmesh.apache.org
[eventmesh-go] 03/26: Update .asf.yml
Posted by ch...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
chenguangsheng pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/eventmesh-go.git
commit 5dfb566e1f3da1782baa58a27217ad94dfe92c70
Author: Eason Chen <qq...@gmail.com>
AuthorDate: Tue Mar 28 20:49:53 2023 +0800
Update .asf.yml
---
.asf.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.asf.yml b/.asf.yml
index 6ae910d..957eb4d 100644
--- a/.asf.yml
+++ b/.asf.yml
@@ -25,7 +25,7 @@ github:
merge: true
rebase: false
protected_branches:
- master:
+ main:
required_status_checks:
strict: true
required_pull_request_reviews:
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@eventmesh.apache.org
For additional commands, e-mail: commits-help@eventmesh.apache.org
[eventmesh-go] 21/26: add ci action
Posted by ch...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
chenguangsheng pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/eventmesh-go.git
commit 39cc21e343ef5d211ea0b43c4d8c9521ea34c234
Author: Alonexc <91...@users.noreply.github.com>
AuthorDate: Mon Apr 24 16:57:38 2023 +0800
add ci action
---
.github/workflows/ci.yml | 63 ++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 63 insertions(+)
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 0000000..845851e
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,63 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+name: CI
+
+on:
+ push:
+ branches: ['*']
+ pull_request:
+ branches: ['*']
+
+jobs:
+ golangci:
+ strategy:
+ matrix:
+ go: [1.17]
+ os: [macos-latest, ubuntu-latest]
+ name: Lint
+ runs-on: ${{ matrix.os }}
+ steps:
+ - uses: actions/checkout@v3
+ - uses: actions/setup-go@v4
+ with:
+ go-version: ${{ matrix.go }}
+ cache: false
+ - name: golangci-lint
+ uses: golangci/golangci-lint-action@v3
+ with:
+ # Required: the version of golangci-lint is required and must be specified without patch version: we always use the latest patch version.
+ version: latest
+
+ license-check:
+ name: License Check
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v3
+
+ - name: Check license header
+ uses: apache/skywalking-eyes@main
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+
+ - name: Check third party dependencies
+ run: |
+ ./gradlew clean jar dist -x test -x checkstyleMain -x javaDoc && ./gradlew installPlugin && ./gradlew tar && sh tools/dependency-check/check-dependencies.sh && echo "Thirty party dependencies check success"
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@eventmesh.apache.org
For additional commands, e-mail: commits-help@eventmesh.apache.org
[eventmesh-go] 24/26: Merge pull request #2 from apache/main
Posted by ch...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
chenguangsheng pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/eventmesh-go.git
commit 29729a9912619424f03ee9d54fb22837069df663
Merge: 811379f 828be3d
Author: Alonexc <91...@users.noreply.github.com>
AuthorDate: Mon May 15 10:03:47 2023 +0800
Merge pull request #2 from apache/main
update
.github/workflows/ci.yml | 59 ++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 59 insertions(+)
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@eventmesh.apache.org
For additional commands, e-mail: commits-help@eventmesh.apache.org
[eventmesh-go] 25/26: add issue template
Posted by ch...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
chenguangsheng pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/eventmesh-go.git
commit 3deba4ebf642afa311d8a09f4f56a67525874d10
Author: Alonexc <al...@163.com>
AuthorDate: Tue May 23 10:21:13 2023 +0800
add issue template
---
.github/ISSUE_TEMPLATE/bug_report.yml | 92 ++++++++++++++++++++++++
.github/ISSUE_TEMPLATE/documentation_related.yml | 60 ++++++++++++++++
.github/ISSUE_TEMPLATE/enhancement_request.yml | 69 ++++++++++++++++++
.github/ISSUE_TEMPLATE/feature_request.yml | 61 ++++++++++++++++
.github/ISSUE_TEMPLATE/question.yml | 51 +++++++++++++
.github/ISSUE_TEMPLATE/unit_test.yml | 75 +++++++++++++++++++
6 files changed, 408 insertions(+)
diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml
new file mode 100644
index 0000000..2c4ad38
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/bug_report.yml
@@ -0,0 +1,92 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+name: Bug report
+title: "[Bug] Bug title"
+description: If something isn't working as expected.
+labels: [ "bug" ]
+body:
+ - type: markdown
+ attributes:
+ value: |
+ For better global communication, Please write in English.
+
+ - type: checkboxes
+ attributes:
+ label: Search before asking
+ description: >
+ Please make sure to search in the [issues](https://github.com/apache/eventmesh-go/issues?q=is%3Aissue)
+ first to see whether the same issue was reported already.
+ options:
+ - label: >
+ I had searched in the [issues](https://github.com/apache/eventmesh-go/issues?q=is%3Aissue) and found
+ no similar issues.
+ required: true
+
+ - type: dropdown
+ attributes:
+ label: Environment
+ description: Describe the environment.
+ options:
+ - Mac
+ - Windows
+ - Linux
+ - Other
+ validations:
+ required: true
+
+ - type: textarea
+ attributes:
+ label: What happened
+ description: Describe what happened.
+ placeholder: >
+ A clear and concise description of what the bug is.
+ validations:
+ required: true
+
+ - type: textarea
+ attributes:
+ label: How to reproduce
+ description: >
+ Describe the steps to reproduce the bug here.
+ placeholder: >
+ Please make sure you provide a reproducible step-by-step case of how to reproduce the problem
+ as minimally and precisely as possible.
+ validations:
+ required: true
+
+ - type: textarea
+ attributes:
+ label: Debug logs
+ description: Anything else we need to know?
+ placeholder: >
+ Add your debug logs here.
+ validations:
+ required: false
+
+ - type: checkboxes
+ attributes:
+ label: Are you willing to submit PR?
+ description: >
+ This is absolutely not required, but we are happy to guide you in the contribution process
+ especially if you already have a good understanding of how to implement the fix.
+ options:
+ - label: Yes I am willing to submit a PR!
+
+ - type: markdown
+ attributes:
+ value: "Thanks for completing our form!"
diff --git a/.github/ISSUE_TEMPLATE/documentation_related.yml b/.github/ISSUE_TEMPLATE/documentation_related.yml
new file mode 100644
index 0000000..7847738
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/documentation_related.yml
@@ -0,0 +1,60 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+name: Documentation Related
+title: "[Doc] Documentation Related"
+description: I find some issues related to the documentation.
+labels: [ "documentation" ]
+body:
+ - type: markdown
+ attributes:
+ value: |
+ For better global communication, Please write in English.
+
+ - type: checkboxes
+ attributes:
+ label: Search before asking
+ description: >
+ Please make sure to search in the [issues](https://github.com/apache/eventmesh-go/issues?q=is%3Aissue)
+ first to see whether the same issue was reported already.
+ options:
+ - label: >
+ I had searched in the [issues](https://github.com/apache/eventmesh-go/issues?q=is%3Aissue) and found
+ no similar issues.
+ required: true
+
+ - type: textarea
+ attributes:
+ label: Documentation Related
+ description: Describe the suggestion about document.
+ placeholder: >
+ e.g There is a typo
+ validations:
+ required: true
+
+ - type: checkboxes
+ attributes:
+ label: Are you willing to submit PR?
+ description: >
+ This is absolutely not required, but we are happy to guide you in the contribution process
+ especially if you already have a good understanding of how to implement the fix.
+ options:
+ - label: Yes I am willing to submit a PR!
+
+ - type: markdown
+ attributes:
+ value: "Thanks for completing our form!"
\ No newline at end of file
diff --git a/.github/ISSUE_TEMPLATE/enhancement_request.yml b/.github/ISSUE_TEMPLATE/enhancement_request.yml
new file mode 100644
index 0000000..2646ca9
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/enhancement_request.yml
@@ -0,0 +1,69 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+name: Enhancement Request
+title: "[Enhancement] Enhancement title"
+description: I want to suggest an enhancement for this project
+labels: [ "enhancement" ]
+body:
+ - type: markdown
+ attributes:
+ value: |
+ For better global communication, Please write in English.
+
+ - type: checkboxes
+ attributes:
+ label: Search before asking
+ description: >
+ Please make sure to search in the [issues](https://github.com/apache/eventmesh-go/issues?q=is%3Aissue)
+ first to see whether the same issue was reported already.
+ options:
+ - label: >
+ I had searched in the [issues](https://github.com/apache/eventmesh-go/issues?q=is%3Aissue) and found
+ no similar issues.
+ required: true
+
+ - type: textarea
+ attributes:
+ label: Enhancement Request
+ description: Describe the suggestion.
+ placeholder: >
+ First of all: Have you checked the docs GitHub issues whether someone else has already reported your issue?
+ validations:
+ required: true
+
+ - type: textarea
+ attributes:
+ label: Describe the solution you'd like
+ description: Describe the suggestion.
+ placeholder: >
+ A clear and concise description of what you want to happen. Add any considered drawbacks.
+ validations:
+ required: true
+
+ - type: checkboxes
+ attributes:
+ label: Are you willing to submit PR?
+ description: >
+ This is absolutely not required, but we are happy to guide you in the contribution process
+ especially if you already have a good understanding of how to implement the fix.
+ options:
+ - label: Yes I am willing to submit a PR!
+
+ - type: markdown
+ attributes:
+ value: "Thanks for completing our form!"
\ No newline at end of file
diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml
new file mode 100644
index 0000000..93aa812
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/feature_request.yml
@@ -0,0 +1,61 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+name: Feature Request
+title: "[Feature] Feature title"
+description: I want to suggest a feature for this project.
+labels: [ "feature" ]
+body:
+ - type: markdown
+ attributes:
+ value: |
+ For better global communication, Please write in English.
+
+ - type: checkboxes
+ attributes:
+ label: Search before asking
+ description: >
+ Please make sure to search in the [issues](https://github.com/apache/eventmesh-go/issues?q=is%3Aissue)
+ first to see whether the same issue was reported already.
+ options:
+ - label: >
+ I had searched in the [issues](https://github.com/apache/eventmesh-go/issues?q=is%3Aissue) and found
+ no similar issues.
+ required: true
+
+ - type: textarea
+ attributes:
+ label: Feature Request
+ description: Describe the feature.
+ placeholder: >
+ First of all: Have you checked the docs GitHub issues whether someone else has already reported your issue?
+ Maybe the feature already exists?
+ validations:
+ required: true
+
+ - type: checkboxes
+ attributes:
+ label: Are you willing to submit PR?
+ description: >
+ This is absolutely not required, but we are happy to guide you in the contribution process
+ especially if you already have a good understanding of how to implement the fix.
+ options:
+ - label: Yes I am willing to submit a PR!
+
+ - type: markdown
+ attributes:
+ value: "Thanks for completing our form!"
diff --git a/.github/ISSUE_TEMPLATE/question.yml b/.github/ISSUE_TEMPLATE/question.yml
new file mode 100644
index 0000000..4291dc6
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/question.yml
@@ -0,0 +1,51 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+name: Question
+title: "[Question] Question title"
+description: I have a question that isn't answered in docs or issue.
+labels: [ "question" ]
+body:
+ - type: markdown
+ attributes:
+ value: |
+ For better global communication, Please write in English.
+
+ - type: checkboxes
+ attributes:
+ label: Search before asking
+ description: >
+ Please make sure to search in the [issues](https://github.com/apache/eventmesh-go/issues?q=is%3Aissue)
+ first to see whether the same issue was reported already.
+ options:
+ - label: >
+ I had searched in the [issues](https://github.com/apache/eventmesh-go/issues?q=is%3Aissue) and found
+ no similar issues.
+ required: true
+
+ - type: textarea
+ attributes:
+ label: Question
+ description: Describe your question.
+ placeholder: >
+ Describe your question here:
+ validations:
+ required: true
+
+ - type: markdown
+ attributes:
+ value: "Thanks for completing our form!"
diff --git a/.github/ISSUE_TEMPLATE/unit_test.yml b/.github/ISSUE_TEMPLATE/unit_test.yml
new file mode 100644
index 0000000..f932dbc
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/unit_test.yml
@@ -0,0 +1,75 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+name: Unit Test
+title: "[Unit Test] Unit test title"
+description: I want to do some unit tests for this project
+labels: [ "testing" ]
+body:
+ - type: markdown
+ attributes:
+ value: |
+ For better global communication, Please write in English.
+
+ - type: checkboxes
+ attributes:
+ label: Search before asking
+ description: >
+ Please make sure to search in the [issues](https://github.com/apache/eventmesh-go/issues?q=is%3Aissue)
+ first to see whether the same issue was reported already.
+ options:
+ - label: >
+ I had searched in the [issues](https://github.com/apache/eventmesh-go/issues?q=is%3Aissue) and found
+ no similar issues.
+ required: true
+
+ - type: textarea
+ attributes:
+ label: Unit test request
+ description: Describe the unit test.
+ placeholder: >
+ First of all: Have you checked the docs GitHub issues whether someone else has already reported your issue?
+ Maybe the unit tests you want to do have already been done?
+ validations:
+ required: true
+
+ - type: textarea
+ attributes:
+ label: Describe the unit tests you want to do
+ description: Describe the unit test.
+ value: |
+ Module name:
+ Located at:
+ Task status: ×(unfinished) / √(finished)
+ | Task Status | Class | Type |
+ | :------: | :------ | :------ |
+ | × | xxxxxx | xxxxxx |
+ validations:
+ required: true
+
+ - type: checkboxes
+ attributes:
+ label: Are you willing to submit PR?
+ description: >
+ This is absolutely not required, but we are happy to guide you in the contribution process
+ especially if you already have a good understanding of how to implement the fix.
+ options:
+ - label: Yes I am willing to submit a PR!
+
+ - type: markdown
+ attributes:
+ value: "Thanks for completing our form!"
\ No newline at end of file
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@eventmesh.apache.org
For additional commands, e-mail: commits-help@eventmesh.apache.org
[eventmesh-go] 10/26: fixes#3775[Enhancement]remove invalid code
Posted by ch...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
chenguangsheng pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/eventmesh-go.git
commit 6c9bf1c3617e3fa3b6cf3d561e51a5c7442c1688
Author: maheshnikam <55...@users.noreply.github.com>
AuthorDate: Fri Apr 21 06:31:46 2023 +0530
fixes#3775[Enhancement]remove invalid code
---
.../standalone/standalone_connector_test.go | 48 ----------------------
1 file changed, 48 deletions(-)
diff --git a/plugin/connector/standalone/standalone_connector_test.go b/plugin/connector/standalone/standalone_connector_test.go
index c6f70b2..0b5e563 100644
--- a/plugin/connector/standalone/standalone_connector_test.go
+++ b/plugin/connector/standalone/standalone_connector_test.go
@@ -147,54 +147,6 @@ func TestConsumer_ManualAck(t *testing.T) {
// TODO update later
func TestConsumer_UpdateOffset(t *testing.T) {
- //sum := atomic.NewInt64(0)
- //ch := make(chan struct{})
- //listener := connector.EventListener{
- // Consume: func(event *ce.Event, commitFunc connector.CommitFunc) error {
- // var data map[string]interface{}
- // event.DataAs(&data)
- // sum.Add(int64(data["val"].(float64)))
- // commitFunc(connector.CommitMessage)
- // ch <- struct{}{}
- // return nil
- // },
- //}
- //
- //factory := &Factory{}
- //err := factory.Setup(pluginName, &plugin.YamlNodeDecoder{
- // Node: &yaml.Node{},
- //})
- //assert.NoError(t, err)
- //consumer, _ := factory.GetConsumer()
- //consumer.Start()
- //defer consumer.Shutdown()
- //consumer.RegisterEventListener(&listener)
- //event := getTestEvent()
- //event.SetExtension("offset", "49")
- //consumer.Subscribe(topicName)
- //consumer.UpdateOffset(context.Background(), []*ce.Event{event})
- //
- //producer, _ := factory.GetProducer()
- //producer.Start()
- //defer producer.Shutdown()
- //for i := 1; i <= 50; i++ {
- // err := producer.Publish(context.Background(), getTestEventOfData(map[string]interface{}{
- // "val": i,
- // }), getEmptyPublishCallback())
- //
- // if err != nil {
- // t.Fail()
- // return
- // }
- //}
- //
- //timer := time.NewTimer(3 * time.Second)
- //select {
- //case <-timer.C:
- // t.Fail()
- //case <-ch:
- // assert.Equal(t, int64(50), sum.Load())
- //}
}
func getTestEvent(topicName string) *ce.Event {
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@eventmesh.apache.org
For additional commands, e-mail: commits-help@eventmesh.apache.org
[eventmesh-go] 04/26: Rename .asf.yml to .asf.yaml
Posted by ch...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
chenguangsheng pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/eventmesh-go.git
commit 4dd0b1161ac1c5e0b348e741354cc335f30cde97
Author: Eason Chen <qq...@gmail.com>
AuthorDate: Tue Mar 28 21:12:31 2023 +0800
Rename .asf.yml to .asf.yaml
---
.asf.yml => .asf.yaml | 0
1 file changed, 0 insertions(+), 0 deletions(-)
diff --git a/.asf.yml b/.asf.yaml
similarity index 100%
rename from .asf.yml
rename to .asf.yaml
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@eventmesh.apache.org
For additional commands, e-mail: commits-help@eventmesh.apache.org
[eventmesh-go] 07/26: Removed invalid code.[producer_test.go] #3775
Posted by ch...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
chenguangsheng pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/eventmesh-go.git
commit 2a03de85b817acd249b4cd4bb644ca4a018d991f
Author: Piyush <11...@users.noreply.github.com>
AuthorDate: Thu Apr 20 18:28:21 2023 +0530
Removed invalid code.[producer_test.go] #3775
---
.../standalone/standalone_connector_test.go | 49 +---------------------
1 file changed, 1 insertion(+), 48 deletions(-)
diff --git a/plugin/connector/standalone/standalone_connector_test.go b/plugin/connector/standalone/standalone_connector_test.go
index c6f70b2..0f48a90 100644
--- a/plugin/connector/standalone/standalone_connector_test.go
+++ b/plugin/connector/standalone/standalone_connector_test.go
@@ -147,54 +147,7 @@ func TestConsumer_ManualAck(t *testing.T) {
// TODO update later
func TestConsumer_UpdateOffset(t *testing.T) {
- //sum := atomic.NewInt64(0)
- //ch := make(chan struct{})
- //listener := connector.EventListener{
- // Consume: func(event *ce.Event, commitFunc connector.CommitFunc) error {
- // var data map[string]interface{}
- // event.DataAs(&data)
- // sum.Add(int64(data["val"].(float64)))
- // commitFunc(connector.CommitMessage)
- // ch <- struct{}{}
- // return nil
- // },
- //}
- //
- //factory := &Factory{}
- //err := factory.Setup(pluginName, &plugin.YamlNodeDecoder{
- // Node: &yaml.Node{},
- //})
- //assert.NoError(t, err)
- //consumer, _ := factory.GetConsumer()
- //consumer.Start()
- //defer consumer.Shutdown()
- //consumer.RegisterEventListener(&listener)
- //event := getTestEvent()
- //event.SetExtension("offset", "49")
- //consumer.Subscribe(topicName)
- //consumer.UpdateOffset(context.Background(), []*ce.Event{event})
- //
- //producer, _ := factory.GetProducer()
- //producer.Start()
- //defer producer.Shutdown()
- //for i := 1; i <= 50; i++ {
- // err := producer.Publish(context.Background(), getTestEventOfData(map[string]interface{}{
- // "val": i,
- // }), getEmptyPublishCallback())
- //
- // if err != nil {
- // t.Fail()
- // return
- // }
- //}
- //
- //timer := time.NewTimer(3 * time.Second)
- //select {
- //case <-timer.C:
- // t.Fail()
- //case <-ch:
- // assert.Equal(t, int64(50), sum.Load())
- //}
+
}
func getTestEvent(topicName string) *ce.Event {
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@eventmesh.apache.org
For additional commands, e-mail: commits-help@eventmesh.apache.org
[eventmesh-go] 02/26: Create .asf.yml
Posted by ch...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
chenguangsheng pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/eventmesh-go.git
commit 88d0b3c302f2c9a537068e97c912acfe12af2f99
Author: Eason Chen <qq...@gmail.com>
AuthorDate: Tue Mar 28 18:29:44 2023 +0800
Create .asf.yml
---
.asf.yml | 42 ++++++++++++++++++++++++++++++++++++++++++
1 file changed, 42 insertions(+)
diff --git a/.asf.yml b/.asf.yml
new file mode 100644
index 0000000..6ae910d
--- /dev/null
+++ b/.asf.yml
@@ -0,0 +1,42 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+github:
+ description: EventMesh go
+ homepage: https://eventmesh.apache.org/
+ labels:
+ - pubsub
+ enabled_merge_buttons:
+ squash: true
+ merge: true
+ rebase: false
+ protected_branches:
+ master:
+ required_status_checks:
+ strict: true
+ required_pull_request_reviews:
+ dismiss_stale_reviews: true
+ required_approving_review_count: 1
+notifications:
+ commits: commits@eventmesh.apache.org
+ # Send all issue emails (new, closed, comments) to issues@
+ issues: issues@eventmesh.apache.org
+ # Send new/closed PR notifications to dev@
+ pullrequests_status: dev@eventmesh.apache.org
+ # Send individual PR comments/reviews to issues@
+ pullrequests_comment: issues@eventmesh.apache.org
+ jira_options: link label worklog
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@eventmesh.apache.org
For additional commands, e-mail: commits-help@eventmesh.apache.org
[eventmesh-go] 19/26: Create golangCI.yml
Posted by ch...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
chenguangsheng pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/eventmesh-go.git
commit 0bd9418b85b79d391335a7cb0fc92cdb28b9c6c9
Author: Alonexc <91...@users.noreply.github.com>
AuthorDate: Mon Apr 24 15:16:47 2023 +0800
Create golangCI.yml
---
.github/workflows/golangCI.yml | 24 ++++++++++++++++++++++++
1 file changed, 24 insertions(+)
diff --git a/.github/workflows/golangCI.yml b/.github/workflows/golangCI.yml
new file mode 100644
index 0000000..c84bf79
--- /dev/null
+++ b/.github/workflows/golangCI.yml
@@ -0,0 +1,24 @@
+name: golangci-lint
+on:
+ push:
+ branches:
+ - master
+ pull_request:
+ branches:
+ - master
+
+jobs:
+ golangci:
+ name: lint
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/setup-go@v4
+ with:
+ go-version: '1.17'
+ cache: false
+ - uses: actions/checkout@v3
+ - name: golangci-lint
+ uses: golangci/golangci-lint-action@v3
+ with:
+ # Optional: version of golangci-lint to use in form of v1.2 or v1.2.3 or `latest` to use the latest version
+ version: latest
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@eventmesh.apache.org
For additional commands, e-mail: commits-help@eventmesh.apache.org
[eventmesh-go] 11/26: Update retry.go
Posted by ch...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
chenguangsheng pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/eventmesh-go.git
commit 904020ca99cf7219b1c7902fc394b5ed9e84a5f2
Author: Harshitha Sudhakar <11...@users.noreply.github.com>
AuthorDate: Sat Apr 22 17:18:01 2023 +0530
Update retry.go
Returns "time.Until(c.ExecuteTime)" at line 32
---
runtime/core/protocol/grpc/retry/retry.go | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/runtime/core/protocol/grpc/retry/retry.go b/runtime/core/protocol/grpc/retry/retry.go
index 7f8ae47..27bb8db 100644
--- a/runtime/core/protocol/grpc/retry/retry.go
+++ b/runtime/core/protocol/grpc/retry/retry.go
@@ -29,5 +29,5 @@ func (c *Retry) SetDelay(delay time.Duration) *Retry {
}
func (c *Retry) GetDelay() time.Duration {
- return c.ExecuteTime.Sub(time.Now())
+ return time.Until(c.ExecuteTime)
}
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@eventmesh.apache.org
For additional commands, e-mail: commits-help@eventmesh.apache.org
[eventmesh-go] 18/26: Merge pull request #1 from apache/main
Posted by ch...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
chenguangsheng pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/eventmesh-go.git
commit 6d41cda10dc76e7a6bf125acbb3fda24ba297966
Merge: 4dd0b11 9dabd4b
Author: Alonexc <91...@users.noreply.github.com>
AuthorDate: Mon Apr 24 10:06:20 2023 +0800
Merge pull request #1 from apache/main
update
.asf.yaml | 5 +++
plugin/connector/rocketmq/consumer_test.go | 25 -----------
plugin/connector/rocketmq/producer_test.go | 25 -----------
.../standalone/standalone_connector_test.go | 49 +---------------------
runtime/core/protocol/grpc/retry/retry.go | 2 +-
5 files changed, 7 insertions(+), 99 deletions(-)
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@eventmesh.apache.org
For additional commands, e-mail: commits-help@eventmesh.apache.org
[eventmesh-go] 23/26: Merge pull request #12 from Alonexc/Alonexc-patch-1
Posted by ch...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
chenguangsheng pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/eventmesh-go.git
commit 828be3d8e24c564a192454aaa35ae18d6650b402
Merge: 9dabd4b bcfa3d8
Author: Eason Chen <qq...@gmail.com>
AuthorDate: Wed May 10 16:31:08 2023 +0800
Merge pull request #12 from Alonexc/Alonexc-patch-1
add golangci-lint ci action
.github/workflows/ci.yml | 59 ++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 59 insertions(+)
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@eventmesh.apache.org
For additional commands, e-mail: commits-help@eventmesh.apache.org
[eventmesh-go] 14/26: Merge branch 'main' into patch-1
Posted by ch...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
chenguangsheng pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/eventmesh-go.git
commit dcefe5039fe4762cac5e90f3d669bd7b2aaac964
Merge: 6c9bf1c be275b7
Author: walleliu <li...@163.com>
AuthorDate: Sun Apr 23 18:52:53 2023 -0700
Merge branch 'main' into patch-1
plugin/connector/standalone/standalone_connector_test.go | 1 +
runtime/core/protocol/grpc/retry/retry.go | 2 +-
2 files changed, 2 insertions(+), 1 deletion(-)
diff --cc plugin/connector/standalone/standalone_connector_test.go
index 0b5e563,0f48a90..deb0410
--- a/plugin/connector/standalone/standalone_connector_test.go
+++ b/plugin/connector/standalone/standalone_connector_test.go
@@@ -147,6 -147,7 +147,7 @@@ func TestConsumer_ManualAck(t *testing.
// TODO update later
func TestConsumer_UpdateOffset(t *testing.T) {
-
++
}
func getTestEvent(topicName string) *ce.Event {
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@eventmesh.apache.org
For additional commands, e-mail: commits-help@eventmesh.apache.org
[eventmesh-go] 26/26: Merge pull request #16 from Alonexc/add_issue_template
Posted by ch...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
chenguangsheng pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/eventmesh-go.git
commit d471d4f99675eaaa35082ba89ed0c7305ba2d544
Merge: 828be3d 3deba4e
Author: Eason Chen <qq...@gmail.com>
AuthorDate: Tue May 23 10:43:24 2023 +0800
Merge pull request #16 from Alonexc/add_issue_template
Add eventmesh-go issue template
.github/ISSUE_TEMPLATE/bug_report.yml | 92 ++++++++++++++++++++++++
.github/ISSUE_TEMPLATE/documentation_related.yml | 60 ++++++++++++++++
.github/ISSUE_TEMPLATE/enhancement_request.yml | 69 ++++++++++++++++++
.github/ISSUE_TEMPLATE/feature_request.yml | 61 ++++++++++++++++
.github/ISSUE_TEMPLATE/question.yml | 51 +++++++++++++
.github/ISSUE_TEMPLATE/unit_test.yml | 75 +++++++++++++++++++
6 files changed, 408 insertions(+)
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@eventmesh.apache.org
For additional commands, e-mail: commits-help@eventmesh.apache.org
[eventmesh-go] 12/26: Merge pull request #3 from piyush280599/patch-1
Posted by ch...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
chenguangsheng pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/eventmesh-go.git
commit 6cedded771500796c47842046e1455c020c2dd4b
Merge: 17fc325 2a03de8
Author: walleliu <li...@163.com>
AuthorDate: Sun Apr 23 00:02:25 2023 -0700
Merge pull request #3 from piyush280599/patch-1
[ISSUE #3775]Removed invalid code.[producer_test.go]
.../standalone/standalone_connector_test.go | 49 +---------------------
1 file changed, 1 insertion(+), 48 deletions(-)
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@eventmesh.apache.org
For additional commands, e-mail: commits-help@eventmesh.apache.org
[eventmesh-go] 01/26: transfer server-go from parent
Posted by ch...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
chenguangsheng pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/eventmesh-go.git
commit 3b1cd0d5adfd832308463de6832451caa18adaca
Author: qqeasonchen <qq...@gmail.com>
AuthorDate: Tue Mar 28 17:23:01 2023 +0800
transfer server-go from parent
---
LICENSE | 201 +++
Makefile | 109 ++
README.md | 15 +
api/openapi-spec/swagger.json | 16 +
api/protobuf-spec/eventmesh.proto | 16 +
assets/REAEMD.md | 1 +
config/common.go | 24 +
config/config.go | 148 ++
config/config_test.go | 93 ++
config/grpc.go | 51 +
config/http.go | 25 +
config/plugin.go | 28 +
config/pprof.go | 25 +
config/tcp.go | 29 +
config/testdata/test_config.yaml | 87 ++
config/tls.go | 34 +
configs/eventmesh-server.yaml | 89 ++
deployments/README.md | 1 +
deployments/docker-compose/eventmesh.yml | 15 +
deployments/helms/REAME.md | 1 +
eventmesh.go | 67 +
examples/README.md | 1 +
examples/webhook/main.go | 84 ++
go.mod | 92 ++
go.sum | 694 +++++++++
log/config.go | 186 +++
log/log.go | 113 ++
log/logger.go | 120 ++
log/logger_factory.go | 155 ++
log/rollwriter/async_roll_writer.go | 133 ++
log/rollwriter/async_roll_writer_options.go | 62 +
log/rollwriter/roll_writer.go | 446 ++++++
log/rollwriter/roll_writer_options.go | 72 +
log/writer_factory.go | 116 ++
log/zaplogger.go | 502 +++++++
pkg/README.md | 1 +
pkg/common/protocol/grpc/keys.go | 65 +
pkg/common/protocol/grpc/message.go | 26 +
pkg/common/protocol/grpc/status_code.go | 68 +
pkg/common/protocol/tcp/pck.go | 97 ++
pkg/naming/registry/instance.go | 34 +
pkg/naming/registry/registry.go | 72 +
pkg/naming/selector/selector.go | 43 +
pkg/util/id.go | 39 +
pkg/util/id_test.go | 94 ++
pkg/util/ip.go | 43 +
pkg/util/pid.go | 32 +
pkg/util/pid_test.go | 37 +
pkg/version/version.go | 20 +
plugin/connector/connector.go | 122 ++
.../connector/rocketmq/client/rocketmq_consumer.go | 159 +++
.../connector/rocketmq/client/rocketmq_producer.go | 168 +++
plugin/connector/rocketmq/config/client_config.go | 41 +
.../rocketmq/constants/message_constants.go | 25 +
.../constants/rocketmq_message_properties.go | 61 +
plugin/connector/rocketmq/consumer.go | 221 +++
plugin/connector/rocketmq/consumer_test.go | 130 ++
.../rocketmq/convert/message_ext_convert.go | 33 +
.../rocketmq/convert/rocketmq_message_reader.go | 110 ++
.../convert/rocketmq_message_reader_test.go | 47 +
.../rocketmq/convert/rocketmq_message_writer.go | 86 ++
.../convert/rocketmq_message_writer_test.go | 44 +
plugin/connector/rocketmq/factory.go | 84 ++
.../rocketmq/mock/mock_rocketmq_consumer.go | 145 ++
.../rocketmq/mock/mock_rocketmq_producer.go | 167 +++
plugin/connector/rocketmq/producer.go | 236 ++++
plugin/connector/rocketmq/producer_test.go | 214 +++
plugin/connector/rocketmq/resource.go | 29 +
plugin/connector/rocketmq/utils/oms_util.go | 27 +
plugin/connector/standalone/broker.go | 259 ++++
plugin/connector/standalone/consumer.go | 200 +++
plugin/connector/standalone/factory.go | 70 +
plugin/connector/standalone/message.go | 47 +
plugin/connector/standalone/producer.go | 116 ++
plugin/connector/standalone/resource.go | 29 +
.../standalone/standalone_connector_test.go | 224 +++
plugin/database/mysql/mysql.go | 68 +
plugin/metrics/metrics.go | 36 +
plugin/metrics/prometheus/config.go | 26 +
plugin/metrics/prometheus/metrics.go | 153 ++
plugin/metrics/prometheus/metrics_test.go | 30 +
plugin/plugin.go | 66 +
plugin/protocol/cloudevents/Readme.md | 1 +
plugin/protocol/cloudevents/cloudevents.go | 215 +++
plugin/protocol/protocol.go | 40 +
plugin/registry/model.go | 42 +
plugin/registry/nacos/config.go | 24 +
.../nacos/mocks/mock_naming_client_interface.go | 211 +++
plugin/registry/nacos/nacos.go | 241 ++++
plugin/registry/nacos/nacos_test.go | 314 +++++
plugin/registry/registry.go | 30 +
plugin/setup.go | 153 ++
plugin/types.go | 37 +
runtime/consts/constant.go | 24 +
runtime/consts/env.go | 32 +
runtime/consts/grpc.go | 23 +
runtime/consts/state.go | 25 +
.../core/protocol/grpc/consumer/consumer_group.go | 74 +
.../grpc/consumer/consumer_group_client.go | 84 ++
.../grpc/consumer/consumer_group_option.go | 286 ++++
.../grpc/consumer/consumer_group_option_test.go | 116 ++
.../protocol/grpc/consumer/consumer_manager.go | 212 +++
.../grpc/consumer/consumer_manager_test.go | 367 +++++
.../core/protocol/grpc/consumer/consumer_mesh.go | 251 ++++
.../protocol/grpc/consumer/consumer_mesh_test.go | 224 +++
.../protocol/grpc/consumer/consumer_processor.go | 317 +++++
.../grpc/consumer/consumer_processor_test.go | 186 +++
.../protocol/grpc/consumer/consumer_service.go | 164 +++
.../grpc/consumer/consumer_service_test.go | 34 +
.../core/protocol/grpc/consumer/message_context.go | 31 +
.../core/protocol/grpc/consumer/message_handler.go | 108 ++
.../core/protocol/grpc/consumer/message_request.go | 248 ++++
.../grpc/consumer/mocks/consumer_group_option.go | 209 +++
.../grpc/consumer/mocks/consumer_manager.go | 148 ++
runtime/core/protocol/grpc/emitter/emitter.go | 43 +
.../core/protocol/grpc/emitter/mocks/emitter.go | 66 +
runtime/core/protocol/grpc/generate_mocks.sh | 27 +
.../protocol/grpc/heartbeat/heartbeat_processor.go | 74 +
.../protocol/grpc/heartbeat/heartbeat_service.go | 70 +
.../core/protocol/grpc/mocks/consumer_service.go | 105 ++
.../core/protocol/grpc/producer/message_context.go | 31 +
.../core/protocol/grpc/producer/producer_group.go | 20 +
.../protocol/grpc/producer/producer_manager.go | 84 ++
.../core/protocol/grpc/producer/producer_mesh.go | 110 ++
.../protocol/grpc/producer/producer_processor.go | 333 +++++
.../protocol/grpc/producer/producer_service.go | 124 ++
.../grpc/producer/producer_service_test.go | 86 ++
runtime/core/protocol/grpc/retry/retry.go | 33 +
runtime/core/protocol/grpc/validator/validator.go | 153 ++
runtime/core/wrapper/base.go | 44 +
runtime/core/wrapper/consumer.go | 96 ++
runtime/core/wrapper/consumer_test.go | 128 ++
runtime/core/wrapper/mocks/consumer.go | 146 ++
runtime/core/wrapper/producer.go | 79 ++
runtime/core/wrapper/producer_test.go | 79 ++
runtime/emserver/graceful.go | 37 +
runtime/emserver/grpc.go | 111 ++
runtime/emserver/grpc_test.go | 39 +
runtime/emserver/http.go | 81 ++
runtime/emserver/mocks/mock_graceful.go | 78 +
runtime/emserver/pprof.go | 61 +
runtime/emserver/pprof_test.go | 42 +
runtime/emserver/tcp.go | 38 +
runtime/proto/pb/README.md | 20 +
runtime/proto/pb/eventmesh-client.pb.go | 1485 ++++++++++++++++++++
runtime/proto/pb/eventmesh-client.proto | 165 +++
runtime/proto/pb/eventmesh-client_grpc.pb.go | 479 +++++++
runtime/proto/pb/mocks/consumer_service.go | 107 ++
runtime/proto/pb/mocks/heartbeat_service.go | 78 +
runtime/proto/pb/mocks/producer_service.go | 108 ++
runtime/registry/registry.go | 23 +
runtime/server.go | 101 ++
scripts/REAEMD.md | 1 +
style/checkStyle.xml | 354 +++++
tests/grpc.md | 199 +++
155 files changed, 18004 insertions(+)
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..261eeb9
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,201 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..5e9dcd7
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,109 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+################################################################################
+# Variables #
+################################################################################
+export GO111MODULE ?= on
+export GOPROXY ?= https://proxy.golang.org
+export GOSUMDB ?= sum.golang.org
+
+# By default, disable CGO_ENABLED. See the details on https://golang.org/cmd/cgo
+CGO ?= 0
+
+LOCAL_ARCH := $(shell uname -m)
+ifeq ($(LOCAL_ARCH),x86_64)
+ TARGET_ARCH_LOCAL=amd64
+else ifeq ($(shell echo $(LOCAL_ARCH) | head -c 5),armv8)
+ TARGET_ARCH_LOCAL=arm64
+else ifeq ($(shell echo $(LOCAL_ARCH) | head -c 4),armv)
+ TARGET_ARCH_LOCAL=arm
+else
+ TARGET_ARCH_LOCAL=amd64
+endif
+export GOARCH ?= $(TARGET_ARCH_LOCAL)
+
+LOCAL_OS := $(shell uname)
+ifeq ($(LOCAL_OS),Linux)
+ TARGET_OS_LOCAL = linux
+else ifeq ($(LOCAL_OS),Darwin)
+ TARGET_OS_LOCAL = darwin
+else
+ TARGET_OS_LOCAL ?= windows
+endif
+export GOOS ?= $(TARGET_OS_LOCAL)
+
+ifeq ($(GOOS),windows)
+BINARY_EXT_LOCAL:=.exe
+GOLANGCI_LINT:=golangci-lint.exe
+BUILDMODE:=-buildmode=exe
+else
+BINARY_EXT_LOCAL:=
+GOLANGCI_LINT:=golangci-lint
+endif
+
+COVERAGE_OPTS = -covermode=set -coverprofile=cover_set.out
+
+
+################################################################################
+# Target: test #
+################################################################################
+.PHONY: test
+test:
+ go test ./...
+
+################################################################################
+# Target: build #
+################################################################################
+.PHONY: build
+build:
+ mkdir -p bin/ && go build -o ./bin
+
+################################################################################
+# Target: coverage #
+################################################################################
+.PHONY: coverage
+coverage:
+ go test ./... $(COVERAGE_OPTS)
+
+################################################################################
+# Target: lint #
+################################################################################
+.PHONY: lint
+lint:
+ $(GOLANGCI_LINT) run --timeout=20m
+
+################################################################################
+# Target: go.mod #
+################################################################################
+.PHONY: go.mod
+go.mod:
+ go mod tidy
+
+################################################################################
+# Target: check-diff #
+################################################################################
+.PHONY: check-diff
+check-diff:
+ git diff --exit-code ./go.mod # check no changes
+
+################################################################################
+# Target: clean #
+################################################################################
+.PHONY: clean
+clean:
+ @-rm -rf *.out
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..ea5903e
--- /dev/null
+++ b/README.md
@@ -0,0 +1,15 @@
+EventMesh Server Go
+- Makefile tip
+ >1. use golangci-lint static code check
+ >>- install: `go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest`
+ >>- `make lint`
+
+ >2. test code
+ >>- `make test`
+
+ >3. test coverage
+ >>- `make coverage`
+
+ >4. build code
+ >>- `make build`
+
diff --git a/api/openapi-spec/swagger.json b/api/openapi-spec/swagger.json
new file mode 100644
index 0000000..d973dce
--- /dev/null
+++ b/api/openapi-spec/swagger.json
@@ -0,0 +1,16 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
\ No newline at end of file
diff --git a/api/protobuf-spec/eventmesh.proto b/api/protobuf-spec/eventmesh.proto
new file mode 100644
index 0000000..d973dce
--- /dev/null
+++ b/api/protobuf-spec/eventmesh.proto
@@ -0,0 +1,16 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
\ No newline at end of file
diff --git a/assets/REAEMD.md b/assets/REAEMD.md
new file mode 100644
index 0000000..cd9d81b
--- /dev/null
+++ b/assets/REAEMD.md
@@ -0,0 +1 @@
+include resource, such png and log
\ No newline at end of file
diff --git a/config/common.go b/config/common.go
new file mode 100644
index 0000000..400c221
--- /dev/null
+++ b/config/common.go
@@ -0,0 +1,24 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package config
+
+type Common struct {
+ Name string `yaml:"name" toml:"name"`
+ RegistryName string `yaml:"registry-name" toml:"registry-name"`
+ Cluster string `yaml:"cluster" toml:"cluster"`
+ Env string `yaml:"env" toml:"env"`
+ IDC string `yaml:"idc" toml:"idc"`
+}
diff --git a/config/config.go b/config/config.go
new file mode 100644
index 0000000..77d11f0
--- /dev/null
+++ b/config/config.go
@@ -0,0 +1,148 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package config
+
+import (
+ "io/ioutil"
+ "sync/atomic"
+ "time"
+
+ "github.com/apache/incubator-eventmesh/eventmesh-server-go/plugin"
+ "gopkg.in/yaml.v3"
+)
+
+// ServerConfigPath is the file path of server config file.
+var ServerConfigPath = defaultConfigPath
+
+const (
+ defaultConfigPath = "./configs/eventmesh-server.yaml"
+)
+
+type Config struct {
+ Common *Common `yaml:"common" toml:"common"`
+ Server struct {
+ *HTTPOption `yaml:"http" toml:"http"`
+ *GRPCOption `yaml:"grpc" toml:"grpc"`
+ *TCPOption `yaml:"tcp" toml:"tcp"`
+ }
+ PProf *PProfOption `yaml:"pprof" toml:"pprof"`
+ ActivePlugins map[string]string `yaml:"active-plugins" toml:"active-plugins"`
+ Plugins plugin.Config `yaml:"plugins,omitempty"`
+}
+
+var globalConfig atomic.Value
+
+func init() {
+ globalConfig.Store(defaultConfig())
+}
+
+func defaultConfig() *Config {
+ cfg := &Config{}
+ cfg.Common = &Common{
+ Name: "eventmesh-server",
+ RegistryName: "eventmesh-go",
+ Cluster: "1",
+ Env: "{}",
+ IDC: "idc1",
+ }
+ cfg.Server.GRPCOption = &GRPCOption{
+ Port: "10010",
+ TLSOption: &TLSOption{
+ EnableInsecure: false,
+ CA: "",
+ Certfile: "",
+ Keyfile: "",
+ },
+ SendPoolSize: 10,
+ SubscribePoolSize: 10,
+ RetryPoolSize: 10,
+ PushMessagePoolSize: 10,
+ ReplyPoolSize: 10,
+ MsgReqNumPerSecond: 5,
+ SessionExpiredInMills: 5 * time.Second,
+ SendMessageTimeout: 5 * time.Second,
+ }
+ cfg.Server.HTTPOption = &HTTPOption{
+ Port: "10010",
+ TLSOption: &TLSOption{
+ EnableInsecure: false,
+ CA: "",
+ Certfile: "",
+ Keyfile: "",
+ },
+ }
+ cfg.Server.TCPOption = &TCPOption{
+ Port: "10010",
+ TLSOption: &TLSOption{
+ EnableInsecure: false,
+ CA: "",
+ Certfile: "",
+ Keyfile: "",
+ },
+ Multicore: false,
+ }
+ cfg.ActivePlugins = map[string]string{
+ "connector": "standalone",
+ "log": "default",
+ }
+ cfg.PProf = &PProfOption{
+ Enable: true,
+ Port: "10011",
+ }
+ cfg.Plugins = map[string]map[string]yaml.Node{
+ "connector": {
+ "standalone": yaml.Node{},
+ },
+ }
+ return cfg
+}
+
+// GlobalConfig returns the global Config.
+func GlobalConfig() *Config {
+ return globalConfig.Load().(*Config)
+}
+
+// SetGlobalConfig set the global Config.
+func SetGlobalConfig(cfg *Config) {
+ globalConfig.Store(cfg)
+}
+
+// LoadGlobalConfig loads a Config from the config file path and sets it as the global Config.
+func LoadGlobalConfig(configPath string) error {
+ cfg, err := LoadConfig(configPath)
+ if err != nil {
+ return err
+ }
+ SetGlobalConfig(cfg)
+ return nil
+}
+
+// LoadConfig loads a Config from the config file path.
+func LoadConfig(configPath string) (*Config, error) {
+ return parseConfigFromFile(configPath)
+}
+
+func parseConfigFromFile(configPath string) (*Config, error) {
+ buf, err := ioutil.ReadFile(configPath)
+ if err != nil {
+ return nil, err
+ }
+ cfg := defaultConfig()
+ if err := yaml.Unmarshal(buf, cfg); err != nil {
+ return nil, err
+ }
+ return cfg, nil
+}
diff --git a/config/config_test.go b/config/config_test.go
new file mode 100644
index 0000000..0e91a1a
--- /dev/null
+++ b/config/config_test.go
@@ -0,0 +1,93 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package config
+
+import (
+ "github.com/apache/incubator-eventmesh/eventmesh-server-go/plugin"
+ testifyassert "github.com/stretchr/testify/assert"
+ "gopkg.in/yaml.v3"
+ "os"
+ "testing"
+ "time"
+)
+
+func TestConfig_Load(t *testing.T) {
+ assert := testifyassert.New(t)
+
+ config := &Config{}
+ config.Common = &Common{
+ RegistryName: "test",
+ Cluster: "test",
+ Env: "env",
+ IDC: "idc1",
+ }
+ config.Server.GRPCOption = &GRPCOption{
+ Port: "10010",
+ TLSOption: &TLSOption{
+ EnableInsecure: false,
+ CA: "",
+ Certfile: "",
+ Keyfile: "",
+ },
+ SendPoolSize: 10,
+ SubscribePoolSize: 10,
+ RetryPoolSize: 10,
+ PushMessagePoolSize: 10,
+ ReplyPoolSize: 10,
+ MsgReqNumPerSecond: 5,
+ SessionExpiredInMills: 5 * time.Second,
+ SendMessageTimeout: 5 * time.Second,
+ }
+ config.Server.HTTPOption = &HTTPOption{
+ Port: "10010",
+ TLSOption: &TLSOption{
+ EnableInsecure: false,
+ CA: "",
+ Certfile: "",
+ Keyfile: "",
+ },
+ }
+ config.Server.TCPOption = &TCPOption{
+ Port: "10010",
+ TLSOption: &TLSOption{
+ EnableInsecure: false,
+ CA: "",
+ Certfile: "",
+ Keyfile: "",
+ },
+ Multicore: false,
+ }
+ config.ActivePlugins = map[string]string{
+ "registry": "nacos",
+ "connector": "rocketmq",
+ "log": "default",
+ }
+ config.PProf = &PProfOption{
+ Enable: true,
+ Port: "10011",
+ }
+ config.Plugins = plugin.Config{}
+
+ configYAML := &Config{}
+ contentYAML, err := os.ReadFile("./testdata/test_config.yaml")
+ assert.NoError(err)
+ if err := yaml.Unmarshal(contentYAML, &configYAML); err != nil {
+ t.Fatal(err)
+ }
+ configYAML.Plugins = plugin.Config{}
+
+ assert.EqualValues(configYAML, config)
+}
diff --git a/config/grpc.go b/config/grpc.go
new file mode 100644
index 0000000..12073eb
--- /dev/null
+++ b/config/grpc.go
@@ -0,0 +1,51 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package config
+
+import "time"
+
+// GRPCOption configuratin for grpc server
+type GRPCOption struct {
+ Port string `yaml:"port" toml:"port"`
+
+ // TLSOption process with the tls configuration
+ *TLSOption `yaml:"tls" toml:"tls"`
+
+ // SendPoolSize pool in handle send msg
+ // default to 10
+ SendPoolSize int `yaml:"send-pool-size" toml:"send-pool-size"`
+ // SubscribePoolSize pool in handle subscribe msg
+ // default to 10
+ SubscribePoolSize int `yaml:"subscribe-pool-size" toml:"subscribe-pool-size"`
+ // RetryPoolSize pool in handle retry msg
+ // default to 10
+ RetryPoolSize int `yaml:"retry-pool-size" toml:"retry-pool-size"`
+ // PushMessagePoolSize pool to push message
+ // default to 10
+ PushMessagePoolSize int `yaml:"push-message-pool-size" toml:"push-message-pool-size"`
+ // ReplyPoolSize pool in handle reply msg
+ // default to 10
+ ReplyPoolSize int `yaml:"reply-pool-size" toml:"reply-pool-size"`
+
+ //MsgReqNumPerSecond
+ MsgReqNumPerSecond float64 `yaml:"msg-req-num-per-second" toml:"msg-req-num-per-second"`
+
+ // SessionExpiredInMills internal to clean the not work session consumer
+ SessionExpiredInMills time.Duration `yaml:"session-expired-in-mills"`
+ // SendMessageTimeout timeout in send message
+ // default to 5s
+ SendMessageTimeout time.Duration `yaml:"send-message-timeout"`
+}
diff --git a/config/http.go b/config/http.go
new file mode 100644
index 0000000..8f6fcd5
--- /dev/null
+++ b/config/http.go
@@ -0,0 +1,25 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package config
+
+// HTTPOption option for http/https server
+type HTTPOption struct {
+ // Port http server listen
+ Port string `yaml:"port" toml:"port"`
+
+ // TLSOption process with the tls configuration
+ *TLSOption `yaml:"tls" toml:"tls"`
+}
diff --git a/config/plugin.go b/config/plugin.go
new file mode 100644
index 0000000..0d399a8
--- /dev/null
+++ b/config/plugin.go
@@ -0,0 +1,28 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package config
+
+var (
+ // ConnectorPluginType plugin type for connector
+ // support standalone and rocketmq
+ // default to standalone
+ ConnectorPluginType = "connector"
+
+ // RegistryPluginType plugin type for connector
+ // support nacos and etcd
+ // default to nacos
+ RegistryPluginType = "registry"
+)
diff --git a/config/pprof.go b/config/pprof.go
new file mode 100644
index 0000000..b328c52
--- /dev/null
+++ b/config/pprof.go
@@ -0,0 +1,25 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package config
+
+// PProfOption option to start the prof
+type PProfOption struct {
+ // Enable the pprof or not
+ Enable bool `yaml:"enable" toml:"enable"`
+ // Port pprof server listen on
+ Port string `yaml:"port" toml:"port"`
+ *TLSOption `yaml:"tls" toml:"tls"`
+}
diff --git a/config/tcp.go b/config/tcp.go
new file mode 100644
index 0000000..7de9d8c
--- /dev/null
+++ b/config/tcp.go
@@ -0,0 +1,29 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package config
+
+// TCPOption option for eventmesh tcp server
+type TCPOption struct {
+ // Port http server listen
+ Port string `yaml:"port" toml:"port"`
+
+ // Multicore indicates whether the engine will
+ // be effectively created with multi-cores
+ Multicore bool `yaml:"multicore" toml:"multicore"`
+
+ // TLSOption process with the tls configuration
+ *TLSOption `yaml:"tls" toml:"tls"`
+}
diff --git a/config/testdata/test_config.yaml b/config/testdata/test_config.yaml
new file mode 100644
index 0000000..6527c71
--- /dev/null
+++ b/config/testdata/test_config.yaml
@@ -0,0 +1,87 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+common:
+ cluster: "test"
+ registry-name: "test"
+ env: "env"
+ idc: "idc1"
+server:
+ grpc:
+ port: 10010
+ tls:
+ enable-secure: false
+ ca: ""
+ certfile: ""
+ keyfile: ""
+ send-pool-size: 10
+ subscribe-pool-size: 10
+ retry-pool-size: 10
+ push-message-pool-size: 10
+ reply-pool-size: 10
+ msg-req-num-per-second: 5
+ session-expired-in-mills: 5s
+ send-message-timeout: 5s
+ http:
+ port: 10010
+ tls:
+ enable-secure: false
+ ca: ""
+ certfile: ""
+ keyfile: ""
+ tcp:
+ port: 10010
+ tls:
+ enable-secure: false
+ ca: ""
+ certfile: ""
+ keyfile: ""
+pprof:
+ enable: true
+ port: 10011
+active-plugins:
+ registry: nacos
+ connector: rocketmq
+ log: default
+plugins:
+ registry:
+ nacos:
+ address_list: your-nacos-addr
+ cache-dir: your-nacos-cache-dir
+ connector:
+ standalone:
+ rocketmq:
+ access_points: your-nameserver-addr
+ namespace: grpcnamespace
+ instance_name: grpcinstance
+ group_name: grpcgroupname
+ send_msg_timeout: 5000
+ producer_retry_times: 3
+ compress_msg_body_threshold: 1024
+ consumer_group: grpcconsumergroup
+ max_reconsume_times: 3
+ message_model: CLUSTERING
+ log:
+ default:
+ - writer: console
+ level: debug
+ - writer: file
+ level: info
+ writer_config:
+ filename: ./eventmesh.log
+ max_size: 10
+ max_backups: 10
+ max_age: 7
+ compress: false
\ No newline at end of file
diff --git a/config/tls.go b/config/tls.go
new file mode 100644
index 0000000..a167baf
--- /dev/null
+++ b/config/tls.go
@@ -0,0 +1,34 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package config
+
+// TLSOption option to tls
+type TLSOption struct {
+ // EnableInsecure enable the insecure request
+ EnableInsecure bool `yaml:"enable-secure" toml:"enable-secure"`
+
+ // CA no client authentication is used,
+ // and the file CA is used to verify the server certificate
+ CA string `yaml:"ca" toml:"ca"`
+
+ // Certfile client authentication is used with the specified cert/key pair.
+ // The server certificate is verified with the system CAs
+ Certfile string `yaml:"certfile" toml:"certfile"`
+
+ // client authentication is used with the specified cert/key pair.
+ // The server certificate is verified using the specified CA file
+ Keyfile string `yaml:"keyfile" toml:"keyfile"`
+}
diff --git a/configs/eventmesh-server.yaml b/configs/eventmesh-server.yaml
new file mode 100644
index 0000000..01d776a
--- /dev/null
+++ b/configs/eventmesh-server.yaml
@@ -0,0 +1,89 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+server:
+ grpc:
+ port: 10010
+ tls:
+ enable-secure: false
+ ca: ""
+ certfile: ""
+ keyfile: ""
+ pprof:
+ port: 10011
+ send-pool-size: 10
+ subscribe-pool-size: 10
+ retry-pool-size: 10
+ push-message-pool-size: 10
+ reply-pool-size: 10
+ msg-req-num-per-second: 5
+ cluster: "test"
+ env: "env"
+ idc: "idc1"
+ session-expired-in-mills: 5s
+ send-message-timeout: 5s
+ http:
+ port: 10010
+ tls:
+ enable-secure: false
+ ca: ""
+ certfile: ""
+ keyfile: ""
+ pprof:
+ port: 10011
+ tcp:
+ port: 10010
+ tls:
+ enable-secure: false
+ ca: ""
+ certfile: ""
+ keyfile: ""
+ pprof:
+ port: 10011
+active-plugins:
+ registry: nacos
+ connector: standalone
+ log: default
+plugins:
+ registry:
+ nacos:
+ address_list: your-nacos-addr
+ cache-dir: your-nacos-cache-dir
+ connector:
+ standalone:
+ rocketmq:
+ access_points: your-nameserver-addr
+ namespace: grpcnamespace
+ instance_name: grpcinstance
+ group_name: grpcgroupname
+ send_msg_timeout: 5000
+ producer_retry_times: 3
+ compress_msg_body_threshold: 1024
+ consumer_group: grpcconsumergroup
+ max_reconsume_times: 3
+ message_model: CLUSTERING
+ log:
+ default:
+ - writer: console
+ level: debug
+ - writer: file
+ level: info
+ writer_config:
+ filename: ./eventmesh.log
+ max_size: 10
+ max_backups: 10
+ max_age: 7
+ compress: false
\ No newline at end of file
diff --git a/deployments/README.md b/deployments/README.md
new file mode 100644
index 0000000..25c9ca2
--- /dev/null
+++ b/deployments/README.md
@@ -0,0 +1 @@
+docker-compose,kubernetes/helm,mesos,terraform,bosh
\ No newline at end of file
diff --git a/deployments/docker-compose/eventmesh.yml b/deployments/docker-compose/eventmesh.yml
new file mode 100644
index 0000000..4f1421a
--- /dev/null
+++ b/deployments/docker-compose/eventmesh.yml
@@ -0,0 +1,15 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
\ No newline at end of file
diff --git a/deployments/helms/REAME.md b/deployments/helms/REAME.md
new file mode 100644
index 0000000..ee342d9
--- /dev/null
+++ b/deployments/helms/REAME.md
@@ -0,0 +1 @@
+helms to deploy eventmesh
\ No newline at end of file
diff --git a/eventmesh.go b/eventmesh.go
new file mode 100644
index 0000000..046b0ce
--- /dev/null
+++ b/eventmesh.go
@@ -0,0 +1,67 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package main
+
+import (
+ "flag"
+ "github.com/apache/incubator-eventmesh/eventmesh-server-go/config"
+ "github.com/apache/incubator-eventmesh/eventmesh-server-go/log"
+ "github.com/apache/incubator-eventmesh/eventmesh-server-go/plugin"
+ "github.com/apache/incubator-eventmesh/eventmesh-server-go/runtime"
+
+ // load all plugin here if you add new plugin you need to import it by _
+ _ "github.com/apache/incubator-eventmesh/eventmesh-server-go/plugin/connector/rocketmq"
+ _ "github.com/apache/incubator-eventmesh/eventmesh-server-go/plugin/connector/standalone"
+ _ "github.com/apache/incubator-eventmesh/eventmesh-server-go/plugin/database/mysql"
+ _ "github.com/apache/incubator-eventmesh/eventmesh-server-go/plugin/metrics/prometheus"
+ _ "github.com/apache/incubator-eventmesh/eventmesh-server-go/plugin/protocol/cloudevents"
+ _ "github.com/apache/incubator-eventmesh/eventmesh-server-go/plugin/registry/nacos"
+)
+
+var confPath string
+
+func init() {
+ flag.StringVar(&confPath, "config", config.ServerConfigPath, "configuration file path")
+}
+
+func main() {
+ cfg, err := config.LoadConfig(confPath)
+ if err != nil {
+ log.Fatalf("load config err:%v", err)
+ }
+ if err := SetupPlugins(cfg); err != nil {
+ log.Fatalf("setup plugin err:%v", err)
+ }
+ config.SetGlobalConfig(cfg)
+
+ if err := runtime.Start(); err != nil {
+ log.Fatalf("start runtime server err:%v", err)
+ }
+
+ log.Infof("stop runtime server success")
+}
+
+// SetupPlugins registers client config and setups plugins according to the Config.
+func SetupPlugins(cfg *config.Config) error {
+ // SetupConfig all plugins
+ if cfg.Plugins != nil {
+ if err := cfg.Plugins.Setup(); err != nil {
+ return err
+ }
+ }
+ plugin.SetActivePlugin(cfg.ActivePlugins)
+ return nil
+}
diff --git a/examples/README.md b/examples/README.md
new file mode 100644
index 0000000..413c0f9
--- /dev/null
+++ b/examples/README.md
@@ -0,0 +1 @@
+how to use eventmesh
\ No newline at end of file
diff --git a/examples/webhook/main.go b/examples/webhook/main.go
new file mode 100644
index 0000000..97f6784
--- /dev/null
+++ b/examples/webhook/main.go
@@ -0,0 +1,84 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package main
+
+import (
+ "flag"
+ "fmt"
+ "github.com/gin-gonic/gin"
+ "io/ioutil"
+ "net/http"
+ "net/url"
+ "strings"
+)
+
+// Response indicate the http response
+type Response struct {
+ RetCode string `json:"retCode"`
+ ErrMsg string `json:"errMsg"`
+}
+
+// OK return the success response
+func OK(data interface{}) *Response {
+ return &Response{
+ RetCode: "0",
+ ErrMsg: "OK",
+ }
+}
+
+// Err return the error response
+func Err(err error, code string) *Response {
+ return &Response{
+ RetCode: code,
+ ErrMsg: err.Error(),
+ }
+}
+
+// provide a webserver to handle the operator event callback
+func main() {
+ flag.Parse()
+ router := gin.Default()
+ printAndReply := func(c *gin.Context) {
+ buf, err := ioutil.ReadAll(c.Request.Body)
+ if err != nil {
+ c.JSON(http.StatusOK, Err(err, "-1"))
+ return
+ }
+ content, err := url.QueryUnescape(string(buf))
+ if err != nil {
+ c.JSON(http.StatusOK, Err(err, "-1"))
+ return
+ }
+
+ fmt.Printf("query content: %s \n", contentEscape(content))
+ c.JSON(http.StatusOK, OK("OK"))
+ }
+ router.Any("/*anypath", func(c *gin.Context) {
+ printAndReply(c)
+ })
+
+ fmt.Println("server start at:18080")
+ // if the web server start failed, we need to stop the server right now
+ if err := router.Run(fmt.Sprintf(":%d", 18080)); err != nil {
+ panic(err)
+ }
+}
+
+func contentEscape(content string) string {
+ escapedContent := strings.Replace(content, "\n", "", -1)
+ escapedContent = strings.Replace(escapedContent, "\r", "", -1)
+ return escapedContent
+}
diff --git a/go.mod b/go.mod
new file mode 100644
index 0000000..373d648
--- /dev/null
+++ b/go.mod
@@ -0,0 +1,92 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+module github.com/apache/incubator-eventmesh/eventmesh-server-go
+
+go 1.18
+
+require (
+ github.com/apache/rocketmq-client-go/v2 v2.1.1
+ github.com/cloudevents/sdk-go/v2 v2.11.0
+ github.com/deckarep/golang-set/v2 v2.1.0
+ github.com/gin-gonic/gin v1.8.1
+ github.com/gogf/gf v1.16.9
+ github.com/golang/mock v1.6.0
+ github.com/google/uuid v1.1.2
+ github.com/hashicorp/go-multierror v1.1.1
+ github.com/json-iterator/go v1.1.12
+ github.com/lestrrat-go/strftime v1.0.6
+ github.com/liyue201/gostl v1.0.1
+ github.com/nacos-group/nacos-sdk-go/v2 v2.1.2
+ github.com/panjf2000/ants/v2 v2.6.0
+ github.com/pkg/errors v0.9.1
+ github.com/prometheus/client_golang v1.13.0
+ github.com/stretchr/testify v1.8.0
+ github.com/unrolled/secure v1.12.0
+ go.uber.org/atomic v1.7.0
+ go.uber.org/fx v1.18.1
+ go.uber.org/zap v1.22.0
+ google.golang.org/grpc v1.48.0
+ google.golang.org/protobuf v1.28.1
+ gopkg.in/yaml.v3 v3.0.1
+)
+
+require (
+ github.com/aliyun/alibaba-cloud-sdk-go v1.61.1704 // indirect
+ github.com/beorn7/perks v1.0.1 // indirect
+ github.com/buger/jsonparser v1.1.1 // indirect
+ github.com/cespare/xxhash/v2 v2.1.2 // indirect
+ github.com/davecgh/go-spew v1.1.1 // indirect
+ github.com/emirpasic/gods v1.12.0 // indirect
+ github.com/gin-contrib/sse v0.1.0 // indirect
+ github.com/go-playground/locales v0.14.0 // indirect
+ github.com/go-playground/universal-translator v0.18.0 // indirect
+ github.com/go-playground/validator/v10 v10.10.0 // indirect
+ github.com/goccy/go-json v0.9.7 // indirect
+ github.com/golang/protobuf v1.5.2 // indirect
+ github.com/hashicorp/errwrap v1.0.0 // indirect
+ github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af // indirect
+ github.com/konsorten/go-windows-terminal-sequences v1.0.3 // indirect
+ github.com/leodido/go-urn v1.2.1 // indirect
+ github.com/mattn/go-isatty v0.0.14 // indirect
+ github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
+ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
+ github.com/modern-go/reflect2 v1.0.2 // indirect
+ github.com/patrickmn/go-cache v2.1.0+incompatible // indirect
+ github.com/pelletier/go-toml/v2 v2.0.1 // indirect
+ github.com/pmezard/go-difflib v1.0.0 // indirect
+ github.com/prometheus/client_model v0.2.0 // indirect
+ github.com/prometheus/common v0.37.0 // indirect
+ github.com/prometheus/procfs v0.8.0 // indirect
+ github.com/satori/go.uuid v1.2.0 // indirect
+ github.com/sirupsen/logrus v1.6.0 // indirect
+ github.com/stathat/consistent v1.0.0 // indirect
+ github.com/tidwall/gjson v1.13.0 // indirect
+ github.com/tidwall/match v1.1.1 // indirect
+ github.com/tidwall/pretty v1.2.0 // indirect
+ github.com/ugorji/go/codec v1.2.7 // indirect
+ go.uber.org/dig v1.15.0 // indirect
+ go.uber.org/multierr v1.6.0 // indirect
+ golang.org/x/crypto v0.5.0 // indirect
+ golang.org/x/net v0.7.0 // indirect
+ golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 // indirect
+ golang.org/x/sys v0.5.0 // indirect
+ golang.org/x/text v0.7.0 // indirect
+ golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9 // indirect
+ google.golang.org/genproto v0.0.0-20200825200019-8632dd797987 // indirect
+ gopkg.in/ini.v1 v1.66.2 // indirect
+ gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect
+ gopkg.in/yaml.v2 v2.4.0 // indirect
+)
diff --git a/go.sum b/go.sum
new file mode 100644
index 0000000..7232fb0
--- /dev/null
+++ b/go.sum
@@ -0,0 +1,694 @@
+cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
+cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
+cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
+cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
+cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
+cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
+cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=
+cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=
+cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=
+cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=
+cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
+cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=
+cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
+cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
+cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
+cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
+cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
+cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
+cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
+cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
+cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
+cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
+cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
+cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
+cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=
+cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
+cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
+cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
+cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
+cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
+dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
+github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+github.com/BurntSushi/toml v1.1.0 h1:ksErzDEI1khOiGPgpwuI7x2ebx/uXQNw7xJpn9Eq1+I=
+github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
+github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
+github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
+github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
+github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
+github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
+github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
+github.com/aliyun/alibaba-cloud-sdk-go v1.61.1704 h1:PpfENOj/vPfhhy9N2OFRjpue0hjM5XqAp2thFmkXXIk=
+github.com/aliyun/alibaba-cloud-sdk-go v1.61.1704/go.mod h1:RcDobYh8k5VP6TNybz9m++gL3ijVI5wueVr0EM10VsU=
+github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
+github.com/apache/rocketmq-client-go/v2 v2.1.1 h1:WY/LkOYSQaVyV+HOqdiIgF4LE3beZ/jwdSLKZlzpabw=
+github.com/apache/rocketmq-client-go/v2 v2.1.1/go.mod h1:GZzExtXY9zpI6FfiVJYAhw2IXQtgnHUuWpULo7nr5lw=
+github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
+github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A=
+github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
+github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
+github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
+github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
+github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs=
+github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0=
+github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
+github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
+github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE=
+github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
+github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
+github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
+github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
+github.com/clbanning/mxj v1.8.5-0.20200714211355-ff02cfb8ea28 h1:LdXxtjzvZYhhUaonAaAKArG3pyC67kGL3YY+6hGG8G4=
+github.com/clbanning/mxj v1.8.5-0.20200714211355-ff02cfb8ea28/go.mod h1:BVjHeAH+rl9rs6f+QIpeRl0tfu10SXn1pUSa5PVGJng=
+github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
+github.com/cloudevents/sdk-go/v2 v2.11.0 h1:pCb7Cdkb8XpUoil+miuw6PEzuCG9cc8Erj8y1/q3odo=
+github.com/cloudevents/sdk-go/v2 v2.11.0/go.mod h1:xDmKfzNjM8gBvjaF8ijFjM1VYOVUEeUfapHMUX1T5To=
+github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
+github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
+github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI=
+github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
+github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
+github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
+github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/deckarep/golang-set/v2 v2.1.0 h1:g47V4Or+DUdzbs8FxCCmgb6VYd+ptPAngjM6dtGktsI=
+github.com/deckarep/golang-set/v2 v2.1.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4=
+github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg=
+github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
+github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
+github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
+github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
+github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
+github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE=
+github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
+github.com/fatih/color v1.12.0 h1:mRhaKNwANqRgUBGKmnI5ZxEk7QXmjQeCcuYFMX2bfcc=
+github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
+github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
+github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
+github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
+github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
+github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
+github.com/gin-gonic/gin v1.8.1 h1:4+fr/el88TOO3ewCmQr8cx/CtZ/umlIRIs5M4NTNjf8=
+github.com/gin-gonic/gin v1.8.1/go.mod h1:ji8BvRH1azfM+SYow9zQ6SZMvR8qOMZHmsCuWR9tTTk=
+github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
+github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
+github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
+github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
+github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
+github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
+github.com/go-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0=
+github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
+github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
+github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
+github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
+github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=
+github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
+github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU=
+github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs=
+github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho=
+github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA=
+github.com/go-playground/validator/v10 v10.10.0 h1:I7mrTYv78z8k8VXa/qJlOlEXn/nBh+BF8dHX5nt/dr0=
+github.com/go-playground/validator/v10 v10.10.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos=
+github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
+github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
+github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
+github.com/goccy/go-json v0.9.7 h1:IcB+Aqpx/iMHu5Yooh7jEzJk1JZ7Pjtmys2ukPr7EeM=
+github.com/goccy/go-json v0.9.7/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
+github.com/gogf/gf v1.16.9 h1:Q803UmmRo59+Ws08sMVFOcd8oNpkSWL9vS33hlo/Cyk=
+github.com/gogf/gf v1.16.9/go.mod h1:8Q/kw05nlVRp+4vv7XASBsMe9L1tsVKiGoeP2AHnlkk=
+github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
+github.com/goji/httpauth v0.0.0-20160601135302-2da839ab0f4d/go.mod h1:nnjvkQ9ptGaCkuDUx6wNykzzlUixGxvkme+H/lnzb+A=
+github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
+github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
+github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
+github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
+github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
+github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
+github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
+github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
+github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
+github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
+github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
+github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
+github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
+github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
+github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
+github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
+github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
+github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
+github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
+github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
+github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
+github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
+github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
+github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
+github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
+github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
+github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
+github.com/gomodule/redigo v1.8.5 h1:nRAxCa+SVsyjSBrtZmG/cqb6VbTmuRzpg/PoTFlpumc=
+github.com/gomodule/redigo v1.8.5/go.mod h1:P9dn9mFrCBvWhGE1wpxx6fgq7BAeLBk+UUUzlpkBYO0=
+github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
+github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
+github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
+github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
+github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
+github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
+github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
+github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
+github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
+github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
+github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
+github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
+github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
+github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
+github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
+github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
+github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
+github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y=
+github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
+github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
+github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
+github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
+github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
+github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
+github.com/grokify/html-strip-tags-go v0.0.1 h1:0fThFwLbW7P/kOiTBs03FsJSV9RM2M/Q/MOnCQxKMo0=
+github.com/grokify/html-strip-tags-go v0.0.1/go.mod h1:2Su6romC5/1VXOQMaWL2yb618ARB8iVo6/DR99A6d78=
+github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
+github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
+github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
+github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
+github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
+github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
+github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
+github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
+github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af h1:pmfjZENx5imkbgOkpRUYLnmbU7UEFbjtDA2hxJ1ichM=
+github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
+github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
+github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
+github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
+github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
+github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
+github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
+github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
+github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
+github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
+github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
+github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
+github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
+github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
+github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
+github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
+github.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8=
+github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
+github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
+github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
+github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
+github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
+github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
+github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
+github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
+github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w=
+github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
+github.com/lestrrat-go/envload v0.0.0-20180220234015-a3eb8ddeffcc h1:RKf14vYWi2ttpEmkA4aQ3j4u9dStX2t4M8UM6qqNsG8=
+github.com/lestrrat-go/envload v0.0.0-20180220234015-a3eb8ddeffcc/go.mod h1:kopuH9ugFRkIXf3YoqHKyrJ9YfUFsckUU9S7B+XP+is=
+github.com/lestrrat-go/strftime v1.0.6 h1:CFGsDEt1pOpFNU+TJB0nhz9jl+K0hZSLE205AhTIGQQ=
+github.com/lestrrat-go/strftime v1.0.6/go.mod h1:f7jQKgV5nnJpYgdEasS+/y7EsTb8ykN2z68n3TtcTaw=
+github.com/liyue201/gostl v1.0.1 h1:VQdvogZ90WpCb5WdG9UxS6r5ulnYEp8VfEMEZXVtpIs=
+github.com/liyue201/gostl v1.0.1/go.mod h1:rgK+T1a0sQ1+CsAonfuD1J8C4iuGfOU9VAt9mmR/m10=
+github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8=
+github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
+github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
+github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
+github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
+github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
+github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
+github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
+github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
+github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
+github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
+github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
+github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
+github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
+github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
+github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
+github.com/nacos-group/nacos-sdk-go/v2 v2.1.2 h1:A8GV6j0rw80I6tTKSav/pTpEgNECYXeFvZCsiLBWGnQ=
+github.com/nacos-group/nacos-sdk-go/v2 v2.1.2/go.mod h1:ys/1adWeKXXzbNWfRNbaFlX/t6HVLWdpsNDvmoWTw0g=
+github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
+github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
+github.com/panjf2000/ants/v2 v2.6.0 h1:xOSpw42m+BMiJ2I33we7h6fYzG4DAlpE1xyI7VS2gxU=
+github.com/panjf2000/ants/v2 v2.6.0/go.mod h1:cU93usDlihJZ5CfRGNDYsiBYvoilLvBF5Qp/BT2GNRE=
+github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
+github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
+github.com/pelletier/go-toml/v2 v2.0.1 h1:8e3L2cCQzLFi2CR4g7vGFuFxX7Jl1kKX8gW+iV0GUKU=
+github.com/pelletier/go-toml/v2 v2.0.1/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo=
+github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
+github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
+github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
+github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
+github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
+github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
+github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY=
+github.com/prometheus/client_golang v1.12.2/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY=
+github.com/prometheus/client_golang v1.13.0 h1:b71QUfeo5M8gq2+evJdTPfZhYMAU0uKPkyPJ7TPsloU=
+github.com/prometheus/client_golang v1.13.0/go.mod h1:vTeo+zgvILHsnnj/39Ou/1fPN5nJFOEMgftOUOmlvYQ=
+github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
+github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
+github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
+github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M=
+github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
+github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
+github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
+github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc=
+github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls=
+github.com/prometheus/common v0.37.0 h1:ccBbHCgIiT9uSoFY0vX8H3zsNR5eLt17/RQLUvn8pXE=
+github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA=
+github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
+github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
+github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
+github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
+github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
+github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo=
+github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4=
+github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
+github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
+github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
+github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8=
+github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
+github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=
+github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
+github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
+github.com/sirupsen/logrus v1.4.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
+github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
+github.com/sirupsen/logrus v1.6.0 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I=
+github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
+github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
+github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
+github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
+github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
+github.com/stathat/consistent v1.0.0 h1:ZFJ1QTRn8npNBKW065raSZ8xfOqhpb8vLOkfp4CcL/U=
+github.com/stathat/consistent v1.0.0/go.mod h1:uajTPbgSygZBJ+V+0mY7meZ8i0XAcZs7AQ6V121XSxw=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
+github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
+github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
+github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
+github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
+github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
+github.com/tidwall/gjson v1.13.0 h1:3TFY9yxOQShrvmjdM76K+jc66zJeT6D3/VFFYCGQf7M=
+github.com/tidwall/gjson v1.13.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
+github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
+github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
+github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
+github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
+github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M=
+github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0=
+github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY=
+github.com/unrolled/secure v1.12.0 h1:7k3jcgLwfjiKkhQde6VbQ3D4KDLtDBqDd/hs3PPANDY=
+github.com/unrolled/secure v1.12.0/go.mod h1:BmF5hyM6tXczk3MpQkFf1hpKSRqCyhqcbiQtiAF7+40=
+github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
+github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
+go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
+go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
+go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
+go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
+go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
+go.opentelemetry.io/otel v1.0.0 h1:qTTn6x71GVBvoafHK/yaRUmFzI4LcONZD0/kXxl5PHI=
+go.opentelemetry.io/otel v1.0.0/go.mod h1:AjRVh9A5/5DE7S+mZtTR6t8vpKKryam+0lREnfmS4cg=
+go.opentelemetry.io/otel/trace v1.0.0 h1:TSBr8GTEtKevYMG/2d21M989r5WJYVimhTHBKVEZuh4=
+go.opentelemetry.io/otel/trace v1.0.0/go.mod h1:PXTWqayeFUlJV1YDNhsJYB184+IvAH814St6o6ajzIs=
+go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
+go.uber.org/atomic v1.5.1/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
+go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
+go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
+go.uber.org/dig v1.15.0 h1:vq3YWr8zRj1eFGC7Gvf907hE0eRjPTZ1d3xHadD6liE=
+go.uber.org/dig v1.15.0/go.mod h1:pKHs0wMynzL6brANhB2hLMro+zalv1osARTviTcqHLM=
+go.uber.org/fx v1.18.1 h1:I7VWkdv4iKcbpH7KVSi9Fe1LGmpJv+pbBIb9NidPb+E=
+go.uber.org/fx v1.18.1/go.mod h1:g0V1KMQ66zIRk8bLu3Ea5Jt2w/cHlOIp4wdRsgh0JaY=
+go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI=
+go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
+go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4=
+go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
+go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw=
+go.uber.org/zap v1.22.0 h1:Zcye5DUgBloQ9BaT4qc9BnjOFog5TvBSAGkJ3Nf70c0=
+go.uber.org/zap v1.22.0/go.mod h1:H4siCOZOrAolnUPJEkfaSjDqyP+BDS0DdDWzwcgt3+U=
+golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
+golang.org/x/crypto v0.5.0 h1:U/0M97KRkSFvyD/3FSmdP5W5swImpNgle/EHFhOsQPE=
+golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU=
+golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
+golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
+golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
+golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
+golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
+golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
+golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
+golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
+golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
+golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
+golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
+golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
+golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
+golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
+golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
+golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
+golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
+golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
+golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
+golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
+golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
+golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
+golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
+golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
+golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
+golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
+golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
+golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
+golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
+golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
+golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
+golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
+golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
+golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
+golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
+golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
+golang.org/x/net v0.0.0-20210520170846-37e1c6afe023/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
+golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
+golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
+golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
+golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g=
+golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
+golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
+golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
+golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
+golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
+golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
+golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
+golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
+golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 h1:uVc8UZUe6tr40fFVnUP5Oj+veunVezqYl9z7DYw9xzw=
+golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
+golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
+golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
+golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
+golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
+golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo=
+golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
+golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9 h1:ftMN5LMiBFjbzleLqtoBZk7KdJwhuybIU+FckUHgoyQ=
+golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
+golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
+golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
+golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
+golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
+golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
+golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
+golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
+golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
+golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
+golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
+golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
+golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
+golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
+golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
+golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
+golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
+google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
+google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
+google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
+google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
+google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
+google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
+google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
+google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
+google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
+google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
+google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
+google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
+google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
+google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=
+google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=
+google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
+google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
+google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
+google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
+google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
+google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
+google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
+google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
+google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
+google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
+google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
+google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
+google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
+google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
+google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
+google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
+google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
+google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
+google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
+google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
+google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA=
+google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=
+google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
+google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
+google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20200825200019-8632dd797987 h1:PDIOdWxZ8eRizhKa1AAvY53xsvLB1cWorMjslvY3VA8=
+google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
+google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
+google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
+google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
+google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
+google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
+google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
+google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
+google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=
+google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
+google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
+google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
+google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
+google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
+google.golang.org/grpc v1.48.0 h1:rQOsyJ/8+ufEDJd/Gdsz7HG220Mh9HAhFHRGnIjda0w=
+google.golang.org/grpc v1.48.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
+google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
+google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
+google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
+google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
+google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
+google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
+google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
+google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
+google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
+google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
+google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
+google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
+google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
+google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=
+google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
+gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
+gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
+gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
+gopkg.in/ini.v1 v1.66.2 h1:XfR1dOYubytKy4Shzc2LHrrGhU0lDCfDGG1yLPmpgsI=
+gopkg.in/ini.v1 v1.66.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
+gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8=
+gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
+gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
+gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
+gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
+honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
+honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
+rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
+rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
+rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
+stathat.com/c/consistent v1.0.0 h1:ezyc51EGcRPJUxfHGSgJjWzJdj3NiMU9pNfLNGiXV0c=
+stathat.com/c/consistent v1.0.0/go.mod h1:QkzMWzcbB+yQBL2AttO6sgsQS/JSTapcDISJalmCDS0=
diff --git a/log/config.go b/log/config.go
new file mode 100644
index 0000000..abb63ef
--- /dev/null
+++ b/log/config.go
@@ -0,0 +1,186 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package log
+
+import (
+ "time"
+
+ "gopkg.in/yaml.v3"
+)
+
+// output name, default support console and file.
+const (
+ OutputConsole = "console"
+ OutputFile = "file"
+)
+
+// Config is the log config. Each log may have multiple outputs.
+type Config []OutputConfig
+
+// OutputConfig is the output config, includes console, file and remote.
+type OutputConfig struct {
+ // Writer is the output of log, such as console or file.
+ Writer string
+ WriteConfig WriteConfig `yaml:"writer_config"`
+
+ // Formatter is the format of log, such as console or json.
+ Formatter string
+ FormatConfig FormatConfig `yaml:"formatter_config"`
+
+ // RemoteConfig is the remote config. It's defined by business and should be registered by
+ // third-party modules.
+ RemoteConfig yaml.Node `yaml:"remote_config"`
+
+ // Level controls the log level, like debug, info or error.
+ Level string
+
+ // CallerSkip controls the nesting depth of log function.
+ CallerSkip int `yaml:"caller_skip"`
+}
+
+// WriteConfig is the local file config.
+type WriteConfig struct {
+ // LogPath is the log path
+ LogPath string `yaml:"log_path"`
+ // Filename is the file name.
+ Filename string `yaml:"filename"`
+ // WriteMode is the log write mod. 1: sync, 2: async, 3: fast(maybe dropped).
+ WriteMode int `yaml:"write_mode"`
+ // RollType is the log rolling type. Split files by size/time, default by size.
+ RollType string `yaml:"roll_type"`
+ // MaxAge is the max expire times(day).
+ MaxAge int `yaml:"max_age"`
+ // MaxBackups is the max backup files.
+ MaxBackups int `yaml:"max_backups"`
+ // Compress defines whether log should be compressed.
+ Compress bool `yaml:"compress"`
+ // MaxSize is the max size of log file(MB).
+ MaxSize int `yaml:"max_size"`
+
+ // TimeUnit splits files by time unit, like year/month/hour/minute, default day.
+ // It takes effect only when split by time.
+ TimeUnit TimeUnit `yaml:"time_unit"`
+}
+
+// FormatConfig is the log format config.
+type FormatConfig struct {
+ // TimeFmt is the time format of log output, default as "2006-01-02 15:04:05.000" on empty.
+ TimeFmt string `yaml:"time_fmt"`
+
+ // TimeKey is the time key of log output, default as "T".
+ TimeKey string `yaml:"time_key"`
+ // LevelKey is the level key of log output, default as "L".
+ LevelKey string `yaml:"level_key"`
+ // NameKey is the name key of log output, default as "N".
+ NameKey string `yaml:"name_key"`
+ // CallerKey is the caller key of log output, default as "C".
+ CallerKey string `yaml:"caller_key"`
+ // FunctionKey is the function key of log output, default as "", which means not to print
+ // function name.
+ FunctionKey string `yaml:"function_key"`
+ // MessageKey is the message key of log output, default as "M".
+ MessageKey string `yaml:"message_key"`
+ // StackTraceKey is the stack trace key of log output, default as "S".
+ StacktraceKey string `yaml:"stacktrace_key"`
+}
+
+// WriteMode is the log write mode, one of 1, 2, 3.
+type WriteMode int
+
+const (
+ // WriteSync writes synchronously.
+ WriteSync = 1
+ // WriteAsync writes asynchronously.
+ WriteAsync = 2
+ // WriteFast writes fast(may drop logs asynchronously).
+ WriteFast = 3
+)
+
+// By which log rolls.
+const (
+ // RollBySize rolls logs by file size.
+ RollBySize = "size"
+ // RollByTime rolls logs by time.
+ RollByTime = "time"
+)
+
+// Some common used time formats.
+const (
+ // TimeFormatMinute is accurate to the minute.
+ TimeFormatMinute = "%Y%m%d%H%M"
+ // TimeFormatHour is accurate to the hour.
+ TimeFormatHour = "%Y%m%d%H"
+ // TimeFormatDay is accurate to the day.
+ TimeFormatDay = "%Y%m%d"
+ // TimeFormatMonth is accurate to the month.
+ TimeFormatMonth = "%Y%m"
+ // TimeFormatYear is accurate to the year.
+ TimeFormatYear = "%Y"
+)
+
+// TimeUnit is the time unit by which files are split, one of minute/hour/day/month/year.
+type TimeUnit string
+
+const (
+ // Minute splits by the minute.
+ Minute = "minute"
+ // Hour splits by the hour.
+ Hour = "hour"
+ // Day splits by the day.
+ Day = "day"
+ // Month splits by the month.
+ Month = "month"
+ // Year splits by the year.
+ Year = "year"
+)
+
+// Format returns a string preceding with `.`. Use TimeFormatDay as default.
+func (t TimeUnit) Format() string {
+ var timeFmt string
+ switch t {
+ case Minute:
+ timeFmt = TimeFormatMinute
+ case Hour:
+ timeFmt = TimeFormatHour
+ case Day:
+ timeFmt = TimeFormatDay
+ case Month:
+ timeFmt = TimeFormatMonth
+ case Year:
+ timeFmt = TimeFormatYear
+ default:
+ timeFmt = TimeFormatDay
+ }
+ return "." + timeFmt
+}
+
+// RotationGap returns the time.Duration for time unit. Use one day as the default.
+func (t TimeUnit) RotationGap() time.Duration {
+ switch t {
+ case Minute:
+ return time.Minute
+ case Hour:
+ return time.Hour
+ case Day:
+ return time.Hour * 24
+ case Month:
+ return time.Hour * 24 * 30
+ case Year:
+ return time.Hour * 24 * 365
+ default:
+ return time.Hour * 24
+ }
+}
diff --git a/log/log.go b/log/log.go
new file mode 100644
index 0000000..6cb84e2
--- /dev/null
+++ b/log/log.go
@@ -0,0 +1,113 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Package log provides a log for the framework and applications.
+package log
+
+import (
+ "fmt"
+
+ "go.uber.org/zap"
+ "go.uber.org/zap/zapcore"
+)
+
+// SetLevel sets log level for different output which may be "0", "1" or "2".
+func SetLevel(output string, level Level) {
+ GetDefaultLogger().SetLevel(output, level)
+}
+
+// GetLevel gets log level for different output.
+func GetLevel(output string) Level {
+ return GetDefaultLogger().GetLevel(output)
+}
+
+// With adds user defined fields to Logger. Field support multiple values.
+func With(fields ...Field) Logger {
+ return GetDefaultLogger().With(fields...)
+}
+
+// RedirectStdLog redirects std log as log level INFO.
+// After redirection, log flag is zero, the prefix is empty.
+// The returned function may be used to recover log flag and prefix, and redirect output to
+// os.Stderr.
+func RedirectStdLog(logger Logger) (func(), error) {
+ return RedirectStdLogAt(logger, zap.InfoLevel)
+}
+
+// RedirectStdLogAt redirects std log with a specific level.
+// After redirection, log flag is zero, the prefix is empty.
+// The returned function may be used to recover log flag and prefix, and redirect output to
+// os.Stderr.
+func RedirectStdLogAt(logger Logger, level zapcore.Level) (func(), error) {
+ if l, ok := logger.(*zapLog); ok {
+ return zap.RedirectStdLogAt(l.logger, level)
+ }
+ if l, ok := logger.(*ZapLogWrapper); ok {
+ return zap.RedirectStdLogAt(l.l.logger, level)
+ }
+ return nil, fmt.Errorf("log: only supports redirecting std logs to zap logger")
+}
+
+// Debug logs to DEBUG log. Arguments are handled in the manner of fmt.Print.
+func Debug(args ...interface{}) {
+ GetDefaultLogger().Debug(args...)
+}
+
+// Debugf logs to DEBUG log. Arguments are handled in the manner of fmt.Printf.
+func Debugf(format string, args ...interface{}) {
+ GetDefaultLogger().Debugf(format, args...)
+}
+
+// Info logs to INFO log. Arguments are handled in the manner of fmt.Print.
+func Info(args ...interface{}) {
+ GetDefaultLogger().Info(args...)
+}
+
+// Infof logs to INFO log. Arguments are handled in the manner of fmt.Printf.
+func Infof(format string, args ...interface{}) {
+ GetDefaultLogger().Infof(format, args...)
+}
+
+// Warn logs to WARNING log. Arguments are handled in the manner of fmt.Print.
+func Warn(args ...interface{}) {
+ GetDefaultLogger().Warn(args...)
+}
+
+// Warnf logs to WARNING log. Arguments are handled in the manner of fmt.Printf.
+func Warnf(format string, args ...interface{}) {
+ GetDefaultLogger().Warnf(format, args...)
+}
+
+// Error logs to ERROR log. Arguments are handled in the manner of fmt.Print.
+func Error(args ...interface{}) {
+ GetDefaultLogger().Error(args...)
+}
+
+// Errorf logs to ERROR log. Arguments are handled in the manner of fmt.Printf.
+func Errorf(format string, args ...interface{}) {
+ GetDefaultLogger().Errorf(format, args...)
+}
+
+// Fatal logs to ERROR log. Arguments are handled in the manner of fmt.Print.
+// All Fatal logs will exit by calling os.Exit(1).
+// Implementations may also call os.Exit() with a non-zero exit code.
+func Fatal(args ...interface{}) {
+ GetDefaultLogger().Fatal(args...)
+}
+
+// Fatalf logs to ERROR log. Arguments are handled in the manner of fmt.Printf.
+func Fatalf(format string, args ...interface{}) {
+ GetDefaultLogger().Fatalf(format, args...)
+}
diff --git a/log/logger.go b/log/logger.go
new file mode 100644
index 0000000..c1ffa78
--- /dev/null
+++ b/log/logger.go
@@ -0,0 +1,120 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package log
+
+import (
+ "io"
+)
+
+// Level is the log level.
+type Level int
+
+// Enums log level constants.
+const (
+ LevelNil Level = iota
+ LevelTrace
+ LevelDebug
+ LevelInfo
+ LevelWarn
+ LevelError
+ LevelFatal
+)
+
+// String turns the LogLevel to string.
+func (lv *Level) String() string {
+ return LevelStrings[*lv]
+}
+
+// LevelStrings is the map from log level to its string representation.
+var LevelStrings = map[Level]string{
+ LevelTrace: "trace",
+ LevelDebug: "debug",
+ LevelInfo: "info",
+ LevelWarn: "warn",
+ LevelError: "error",
+ LevelFatal: "fatal",
+}
+
+// LevelNames is the map from string to log level.
+var LevelNames = map[string]Level{
+ "trace": LevelTrace,
+ "debug": LevelDebug,
+ "info": LevelInfo,
+ "warn": LevelWarn,
+ "error": LevelError,
+ "fatal": LevelFatal,
+}
+
+// LoggerOptions is the log options.
+type LoggerOptions struct {
+ LogLevel Level
+ Pattern string
+ Writer io.Writer
+}
+
+// LoggerOption modifies the LoggerOptions.
+type LoggerOption func(*LoggerOptions)
+
+// Field is the user defined log field.
+type Field struct {
+ Key string
+ Value interface{}
+}
+
+// Logger is the underlying logging work for server.
+type Logger interface {
+ // Trace logs to TRACE log. Arguments are handled in the manner of fmt.Print.
+ Trace(args ...interface{})
+ // Tracef logs to TRACE log. Arguments are handled in the manner of fmt.Printf.
+ Tracef(format string, args ...interface{})
+ // Debug logs to DEBUG log. Arguments are handled in the manner of fmt.Print.
+ Debug(args ...interface{})
+ // Debugf logs to DEBUG log. Arguments are handled in the manner of fmt.Printf.
+ Debugf(format string, args ...interface{})
+ // Info logs to INFO log. Arguments are handled in the manner of fmt.Print.
+ Info(args ...interface{})
+ // Infof logs to INFO log. Arguments are handled in the manner of fmt.Printf.
+ Infof(format string, args ...interface{})
+ // Warn logs to WARNING log. Arguments are handled in the manner of fmt.Print.
+ Warn(args ...interface{})
+ // Warnf logs to WARNING log. Arguments are handled in the manner of fmt.Printf.
+ Warnf(format string, args ...interface{})
+ // Error logs to ERROR log. Arguments are handled in the manner of fmt.Print.
+ Error(args ...interface{})
+ // Errorf logs to ERROR log. Arguments are handled in the manner of fmt.Printf.
+ Errorf(format string, args ...interface{})
+ // Fatal logs to ERROR log. Arguments are handled in the manner of fmt.Print.
+ // All Fatal logs will exit by calling os.Exit(1).
+ // Implementations may also call os.Exit() with a non-zero exit code.
+ Fatal(args ...interface{})
+ // Fatalf logs to ERROR log. Arguments are handled in the manner of fmt.Printf.
+ Fatalf(format string, args ...interface{})
+
+ // Sync calls the underlying Core's Sync method, flushing any buffered log entries.
+ // Applications should take care to call Sync before exiting.
+ Sync() error
+
+ // SetLevel set the output log level.
+ SetLevel(output string, level Level)
+ // GetLevel get the output log level.
+ GetLevel(output string) Level
+ // WithFields set some user defined data to logs, such as uid, imei, etc.
+ // Fields must be paired.
+ // Deprecated: use With instead.
+ WithFields(fields ...string) Logger
+ // With add user defined fields to Logger. Fields support multiple values.
+ With(fields ...Field) Logger
+}
diff --git a/log/logger_factory.go b/log/logger_factory.go
new file mode 100644
index 0000000..b53e19f
--- /dev/null
+++ b/log/logger_factory.go
@@ -0,0 +1,155 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package log
+
+import (
+ "errors"
+ "fmt"
+ "sync"
+
+ "github.com/apache/incubator-eventmesh/eventmesh-server-go/plugin"
+ "go.uber.org/zap"
+ "go.uber.org/zap/zapcore"
+)
+
+func init() {
+ RegisterWriter(OutputConsole, DefaultConsoleWriterFactory)
+ RegisterWriter(OutputFile, DefaultFileWriterFactory)
+ Register(defaultLoggerName, NewZapLog(defaultConfig))
+ plugin.Register(defaultLoggerName, DefaultLogFactory)
+}
+
+const (
+ pluginType = "log"
+ defaultLoggerName = "default"
+)
+
+var (
+ // DefaultLogger the default Logger. The initial output is console. When frame start, it is
+ // over write by configuration.
+ DefaultLogger Logger
+ // DefaultLogFactory is the default log loader. Users may replace it with their own
+ // implementation.
+ DefaultLogFactory = &Factory{}
+
+ mu sync.RWMutex
+ loggers = make(map[string]Logger)
+)
+
+// Register registers Logger. It supports multiple Logger implementation.
+func Register(name string, logger Logger) {
+ mu.Lock()
+ defer mu.Unlock()
+ if logger == nil {
+ panic("log: Register logger is nil")
+ }
+ if _, dup := loggers[name]; dup && name != defaultLoggerName {
+ panic("log: Register called twice for logger name " + name)
+ }
+ loggers[name] = logger
+ if name == defaultLoggerName {
+ DefaultLogger = logger
+ }
+}
+
+// GetDefaultLogger gets the default Logger.
+// To configure it, set key in configuration file to default.
+// The console output is the default value.
+func GetDefaultLogger() Logger {
+ mu.RLock()
+ l := DefaultLogger
+ mu.RUnlock()
+ return l
+}
+
+// SetLogger sets the default Logger.
+func SetLogger(logger Logger) {
+ mu.Lock()
+ DefaultLogger = logger
+ mu.Unlock()
+}
+
+// Get returns the Logger implementation by log name.
+// log.Debug use DefaultLogger to print logs. You may also use log.Get("name").Debug.
+func Get(name string) Logger {
+ mu.RLock()
+ l := loggers[name]
+ mu.RUnlock()
+ return l
+}
+
+// Decoder decodes the log.
+type Decoder struct {
+ OutputConfig *OutputConfig
+ Core zapcore.Core
+ ZapLevel zap.AtomicLevel
+}
+
+// Decode decodes writer configuration, copy one.
+func (d *Decoder) Decode(cfg interface{}) error {
+ output, ok := cfg.(**OutputConfig)
+ if !ok {
+ return fmt.Errorf("decoder config type:%T invalid, not **OutputConfig", cfg)
+ }
+ *output = d.OutputConfig
+ return nil
+}
+
+// Factory is the log plugin factory.
+// When server start, the configuration is feed to Factory to generate a log instance.
+type Factory struct {
+}
+
+// Type returns the log plugin type.
+func (f *Factory) Type() string {
+ return pluginType
+}
+
+// Setup starts, load and register logs.
+func (f *Factory) Setup(name string, dec plugin.Decoder) error {
+ if dec == nil {
+ return errors.New("log config decoder empty")
+ }
+ cfg, callerSkip, err := f.setupConfig(dec)
+ if err != nil {
+ return err
+ }
+ logger := NewZapLogWithCallerSkip(cfg, callerSkip)
+ if logger == nil {
+ return errors.New("new zap logger fail")
+ }
+ Register(name, logger)
+ return nil
+}
+
+func (f *Factory) setupConfig(configDec plugin.Decoder) (Config, int, error) {
+ cfg := Config{}
+ if err := configDec.Decode(&cfg); err != nil {
+ return nil, 0, err
+ }
+ if len(cfg) == 0 {
+ return nil, 0, errors.New("log config output empty")
+ }
+
+ // If caller skip is not configured, use 2 as default.
+ callerSkip := 2
+ for i := 0; i < len(cfg); i++ {
+ if cfg[i].CallerSkip != 0 {
+ callerSkip = cfg[i].CallerSkip
+ }
+ }
+ return cfg, callerSkip, nil
+}
diff --git a/log/rollwriter/async_roll_writer.go b/log/rollwriter/async_roll_writer.go
new file mode 100644
index 0000000..e39e7ec
--- /dev/null
+++ b/log/rollwriter/async_roll_writer.go
@@ -0,0 +1,133 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package rollwriter
+
+import (
+ "bytes"
+ "errors"
+ "io"
+ "time"
+
+ "github.com/hashicorp/go-multierror"
+)
+
+// AsyncRollWriter is the asynchronous rolling log writer which implements zapcore.WriteSyncer.
+type AsyncRollWriter struct {
+ logger io.WriteCloser
+ opts *AsyncOptions
+
+ logQueue chan []byte
+ sync chan struct{}
+ syncErr chan error
+ close chan struct{}
+ closeErr chan error
+}
+
+// NewAsyncRollWriter create a new AsyncRollWriter.
+func NewAsyncRollWriter(logger io.WriteCloser, opt ...AsyncOption) *AsyncRollWriter {
+ opts := &AsyncOptions{
+ LogQueueSize: 10000, // default queue size as 10000
+ WriteLogSize: 4 * 1024, // default write log size as 4K
+ WriteLogInterval: 100, // default sync interval as 100ms
+ DropLog: false, // default do not drop logs
+ }
+
+ for _, o := range opt {
+ o(opts)
+ }
+
+ w := &AsyncRollWriter{}
+ w.logger = logger
+ w.opts = opts
+ w.logQueue = make(chan []byte, opts.LogQueueSize)
+ w.sync = make(chan struct{})
+ w.syncErr = make(chan error)
+ w.close = make(chan struct{})
+ w.closeErr = make(chan error)
+
+ // start a new goroutine write batch logs.
+ go w.batchWriteLog()
+ return w
+}
+
+// Write writes logs. It implements io.Writer.
+func (w *AsyncRollWriter) Write(data []byte) (int, error) {
+ log := make([]byte, len(data))
+ copy(log, data)
+ if w.opts.DropLog {
+ select {
+ case w.logQueue <- log:
+ default:
+ return 0, errors.New("log queue is full")
+ }
+ } else {
+ w.logQueue <- log
+ }
+ return len(data), nil
+}
+
+// Sync syncs logs. It implements zapcore.WriteSyncer.
+func (w *AsyncRollWriter) Sync() error {
+ w.sync <- struct{}{}
+ return <-w.syncErr
+}
+
+// Close closes current log file. It implements io.Closer.
+func (w *AsyncRollWriter) Close() error {
+ err := w.Sync()
+ close(w.close)
+ return multierror.Append(err, <-w.closeErr).ErrorOrNil()
+}
+
+// batchWriteLog asynchronously writes logs in batches.
+func (w *AsyncRollWriter) batchWriteLog() {
+ buffer := bytes.NewBuffer(make([]byte, 0, w.opts.WriteLogSize*2))
+ ticker := time.NewTicker(time.Millisecond * time.Duration(w.opts.WriteLogInterval))
+ defer ticker.Stop()
+
+ for {
+ select {
+ case <-ticker.C:
+ if buffer.Len() > 0 {
+ _, _ = w.logger.Write(buffer.Bytes())
+ buffer.Reset()
+ }
+ case data := <-w.logQueue:
+ buffer.Write(data)
+ if buffer.Len() >= w.opts.WriteLogSize {
+ _, _ = w.logger.Write(buffer.Bytes())
+ buffer.Reset()
+ }
+ case <-w.sync:
+ var err error
+ if buffer.Len() > 0 {
+ _, e := w.logger.Write(buffer.Bytes())
+ err = multierror.Append(err, e).ErrorOrNil()
+ buffer.Reset()
+ }
+ size := len(w.logQueue)
+ for i := 0; i < size; i++ {
+ v := <-w.logQueue
+ _, e := w.logger.Write(v)
+ err = multierror.Append(err, e).ErrorOrNil()
+ }
+ w.syncErr <- err
+ case <-w.close:
+ w.closeErr <- w.logger.Close()
+ return
+ }
+ }
+}
diff --git a/log/rollwriter/async_roll_writer_options.go b/log/rollwriter/async_roll_writer_options.go
new file mode 100644
index 0000000..c3a43f4
--- /dev/null
+++ b/log/rollwriter/async_roll_writer_options.go
@@ -0,0 +1,62 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package rollwriter
+
+// AsyncOptions is the call options of AsyncRollWriter.
+type AsyncOptions struct {
+ // LogQueueSize is the queue size of asynchronous log.
+ LogQueueSize int
+
+ // WriteLogSize is the threshold to write async log.
+ WriteLogSize int
+
+ // WriteLogInterval is the time interval to write async log.
+ WriteLogInterval int
+
+ // DropLog determines whether to discard logs when log queue is full.
+ DropLog bool
+}
+
+// AsyncOption modifies the AsyncOptions.
+type AsyncOption func(*AsyncOptions)
+
+// WithLogQueueSize returns an AsyncOption which sets log queue size.
+func WithLogQueueSize(n int) AsyncOption {
+ return func(o *AsyncOptions) {
+ o.LogQueueSize = n
+ }
+}
+
+// WithWriteLogSize returns an AsyncOption which sets log size(Byte) threshold.
+func WithWriteLogSize(n int) AsyncOption {
+ return func(o *AsyncOptions) {
+ o.WriteLogSize = n
+ }
+}
+
+// WithWriteLogInterval returns an AsyncOption which sets log interval(ms) threshold(ms).
+func WithWriteLogInterval(n int) AsyncOption {
+ return func(o *AsyncOptions) {
+ o.WriteLogInterval = n
+ }
+}
+
+// WithDropLog returns an AsyncOption which set whether to drop logs on log queue full.
+func WithDropLog(b bool) AsyncOption {
+ return func(o *AsyncOptions) {
+ o.DropLog = b
+ }
+}
diff --git a/log/rollwriter/roll_writer.go b/log/rollwriter/roll_writer.go
new file mode 100644
index 0000000..2a5f75d
--- /dev/null
+++ b/log/rollwriter/roll_writer.go
@@ -0,0 +1,446 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Package rollwriter provides a high performance rolling file log.
+// It can coordinate with any logs which depends on io.Writer, such as golang standard log.
+// Main features:
+// 1. support rolling logs by file size.
+// 2. support rolling logs by datetime.
+// 3. support scavenging expired or useless logs.
+// 4. support compressing logs.
+package rollwriter
+
+import (
+ "compress/gzip"
+ "errors"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "sort"
+ "strings"
+ "sync"
+ "sync/atomic"
+ "time"
+
+ "github.com/lestrrat-go/strftime"
+)
+
+const (
+ backupTimeFormat = "bk-20060102-150405.00000"
+ compressSuffix = ".gz"
+)
+
+// ensure we always implement io.WriteCloser.
+var _ io.WriteCloser = (*RollWriter)(nil)
+
+// RollWriter is a file log writer which support rolling by size or datetime.
+// It implements io.WriteCloser.
+type RollWriter struct {
+ filePath string
+ opts *Options
+
+ pattern *strftime.Strftime
+ currDir string
+ currPath string
+ currSize int64
+ currFile atomic.Value
+ openTime int64
+
+ mu sync.Mutex
+ notifyOnce sync.Once
+ notifyCh chan bool
+ closeOnce sync.Once
+ closeCh chan *os.File
+}
+
+// NewRollWriter creates a new RollWriter.
+func NewRollWriter(filePath string, opt ...Option) (*RollWriter, error) {
+ opts := &Options{
+ MaxSize: 0, // default no rolling by file size
+ MaxAge: 0, // default no scavenging on expired logs
+ MaxBackups: 0, // default no scavenging on redundant logs
+ Compress: false, // default no compressing
+ }
+
+ // opt has the highest priority and should overwrite the original one.
+ for _, o := range opt {
+ o(opts)
+ }
+
+ if filePath == "" {
+ return nil, errors.New("invalid file path")
+ }
+
+ pattern, err := strftime.New(filePath + opts.TimeFormat)
+ if err != nil {
+ return nil, errors.New("invalid time pattern")
+ }
+
+ w := &RollWriter{
+ filePath: filePath,
+ opts: opts,
+ pattern: pattern,
+ currDir: filepath.Dir(filePath),
+ }
+
+ if err := os.MkdirAll(w.currDir, 0755); err != nil {
+ return nil, err
+ }
+
+ return w, nil
+}
+
+// Write writes logs. It implements io.Writer.
+func (w *RollWriter) Write(v []byte) (n int, err error) {
+ // reopen file every 10 seconds.
+ if w.getCurrFile() == nil || time.Now().Unix()-atomic.LoadInt64(&w.openTime) > 10 {
+ w.mu.Lock()
+ w.reopenFile()
+ w.mu.Unlock()
+ }
+
+ // return when failed to open the file.
+ if w.getCurrFile() == nil {
+ return 0, errors.New("open file fail")
+ }
+
+ // write logs to file.
+ n, err = w.getCurrFile().Write(v)
+ atomic.AddInt64(&w.currSize, int64(n))
+
+ // rolling on full
+ if w.opts.MaxSize > 0 && atomic.LoadInt64(&w.currSize) >= w.opts.MaxSize {
+ w.mu.Lock()
+ w.backupFile()
+ w.mu.Unlock()
+ }
+ return n, err
+}
+
+// Close closes the current log file. It implements io.Closer.
+func (w *RollWriter) Close() error {
+ if w.getCurrFile() == nil {
+ return nil
+ }
+ err := w.getCurrFile().Close()
+ w.setCurrFile(nil)
+
+ if w.notifyCh != nil {
+ close(w.notifyCh)
+ w.notifyCh = nil
+ }
+
+ if w.closeCh != nil {
+ close(w.closeCh)
+ w.closeCh = nil
+ }
+
+ return err
+}
+
+// getCurrFile returns the current log file.
+func (w *RollWriter) getCurrFile() *os.File {
+ if file, ok := w.currFile.Load().(*os.File); ok {
+ return file
+ }
+ return nil
+}
+
+// setCurrFile sets the current log file.
+func (w *RollWriter) setCurrFile(file *os.File) {
+ w.currFile.Store(file)
+}
+
+// reopenFile reopen the file regularly. It notifies the scavenger if file path has changed.
+func (w *RollWriter) reopenFile() {
+ if w.getCurrFile() == nil || time.Now().Unix()-atomic.LoadInt64(&w.openTime) > 10 {
+ atomic.StoreInt64(&w.openTime, time.Now().Unix())
+ currPath := w.pattern.FormatString(time.Now())
+ if w.currPath != currPath {
+ w.currPath = currPath
+ w.notify()
+ }
+ _ = w.doReopenFile(w.currPath)
+ }
+}
+
+// doReopenFile reopen the file.
+func (w *RollWriter) doReopenFile(path string) error {
+ atomic.StoreInt64(&w.openTime, time.Now().Unix())
+ lastFile := w.getCurrFile()
+ of, err := os.OpenFile(path, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0666)
+ if err == nil {
+ w.setCurrFile(of)
+ if lastFile != nil {
+ // delay closing until not used.
+ w.delayCloseFile(lastFile)
+ }
+ st, _ := os.Stat(path)
+ if st != nil {
+ atomic.StoreInt64(&w.currSize, st.Size())
+ }
+ }
+ return err
+}
+
+// backupFile backs this file up and reopen a new one if file size is too large.
+func (w *RollWriter) backupFile() {
+ if w.opts.MaxSize > 0 && atomic.LoadInt64(&w.currSize) >= w.opts.MaxSize {
+ atomic.StoreInt64(&w.currSize, 0)
+
+ // rename the old file.
+ newName := w.currPath + "." + time.Now().Format(backupTimeFormat)
+ if _, e := os.Stat(w.currPath); !os.IsNotExist(e) {
+ _ = os.Rename(w.currPath, newName)
+ }
+
+ // reopen a new one.
+ _ = w.doReopenFile(w.currPath)
+ w.notify()
+ }
+}
+
+// notify runs scavengers.
+func (w *RollWriter) notify() {
+ w.notifyOnce.Do(func() {
+ w.notifyCh = make(chan bool, 1)
+ go w.runCleanFiles()
+ })
+ select {
+ case w.notifyCh <- true:
+ default:
+ }
+}
+
+// runCleanFiles cleans redundant or expired (compressed) logs in a new goroutine.
+func (w *RollWriter) runCleanFiles() {
+ for range w.notifyCh {
+ if w.opts.MaxBackups == 0 && w.opts.MaxAge == 0 && !w.opts.Compress {
+ continue
+ }
+ w.cleanFiles()
+ }
+}
+
+// delayCloseFile delay closing file
+func (w *RollWriter) delayCloseFile(file *os.File) {
+ w.closeOnce.Do(func() {
+ w.closeCh = make(chan *os.File, 100)
+ go w.runCloseFiles()
+ })
+ w.closeCh <- file
+}
+
+// runCloseFiles delay closing file in a new goroutine.
+func (w *RollWriter) runCloseFiles() {
+ for f := range w.closeCh {
+ // delay 20ms
+ time.Sleep(20 * time.Millisecond)
+ f.Close()
+ }
+}
+
+// cleanFiles cleans redundant or expired (compressed) logs.
+func (w *RollWriter) cleanFiles() {
+ // get the file list of current log.
+ files, err := w.getOldLogFiles()
+ if err != nil || len(files) == 0 {
+ return
+ }
+
+ // find the oldest files to scavenge.
+ var compress, remove []logInfo
+ files = filterByMaxBackups(files, &remove, w.opts.MaxBackups)
+
+ // find the expired files by last modified time.
+ files = filterByMaxAge(files, &remove, w.opts.MaxAge)
+
+ // find files to compress by file extension .gz.
+ filterByCompressExt(files, &compress, w.opts.Compress)
+
+ // delete expired or redundant files.
+ w.removeFiles(remove)
+
+ // compress log files.
+ w.compressFiles(compress)
+}
+
+// getOldLogFiles returns the log file list ordered by modified time.
+func (w *RollWriter) getOldLogFiles() ([]logInfo, error) {
+ files, err := ioutil.ReadDir(w.currDir)
+ if err != nil {
+ return nil, fmt.Errorf("can't read log file directory: %s", err)
+ }
+ logFiles := []logInfo{}
+ filename := filepath.Base(w.filePath)
+ for _, f := range files {
+ if f.IsDir() {
+ continue
+ }
+
+ if modTime, err := w.matchLogFile(f.Name(), filename); err == nil {
+ logFiles = append(logFiles, logInfo{modTime, f})
+ }
+ }
+ sort.Sort(byFormatTime(logFiles))
+ return logFiles, nil
+}
+
+// matchLogFile checks whether current log file matches all relative log files, if matched, returns
+// the modified time.
+func (w *RollWriter) matchLogFile(filename, filePrefix string) (time.Time, error) {
+ // exclude current log file.
+ // a.log
+ // a.log.20200712
+ if filepath.Base(w.currPath) == filename {
+ return time.Time{}, errors.New("ignore current logfile")
+ }
+
+ // match all log files with current log file.
+ // a.log -> a.log.20200712-1232/a.log.20200712-1232.gz
+ // a.log.20200712 -> a.log.20200712.20200712-1232/a.log.20200712.20200712-1232.gz
+ if !strings.HasPrefix(filename, filePrefix) {
+ return time.Time{}, errors.New("mismatched prefix")
+ }
+
+ if st, _ := os.Stat(filepath.Join(w.currDir, filename)); st != nil {
+ return st.ModTime(), nil
+ }
+ return time.Time{}, errors.New("file stat fail")
+}
+
+// removeFiles deletes expired or redundant log files.
+func (w *RollWriter) removeFiles(remove []logInfo) {
+ // clean expired or redundant files.
+ for _, f := range remove {
+ os.Remove(filepath.Join(w.currDir, f.Name()))
+ }
+}
+
+// compressFiles compresses demanded log files.
+func (w *RollWriter) compressFiles(compress []logInfo) {
+ // compress log files.
+ for _, f := range compress {
+ fn := filepath.Join(w.currDir, f.Name())
+ _ = compressFile(fn, fn+compressSuffix)
+ }
+}
+
+// filterByMaxBackups filters redundant files that exceeded the limit.
+func filterByMaxBackups(files []logInfo, remove *[]logInfo, maxBackups int) []logInfo {
+ if maxBackups == 0 || len(files) < maxBackups {
+ return files
+ }
+ var remaining []logInfo
+ preserved := make(map[string]bool)
+ for _, f := range files {
+ fn := strings.TrimSuffix(f.Name(), compressSuffix)
+ preserved[fn] = true
+
+ if len(preserved) > maxBackups {
+ *remove = append(*remove, f)
+ } else {
+ remaining = append(remaining, f)
+ }
+ }
+ return remaining
+}
+
+// filterByMaxAge filters expired files.
+func filterByMaxAge(files []logInfo, remove *[]logInfo, maxAge int) []logInfo {
+ if maxAge <= 0 {
+ return files
+ }
+ var remaining []logInfo
+ diff := time.Duration(int64(24*time.Hour) * int64(maxAge))
+ cutoff := time.Now().Add(-1 * diff)
+ for _, f := range files {
+ if f.timestamp.Before(cutoff) {
+ *remove = append(*remove, f)
+ } else {
+ remaining = append(remaining, f)
+ }
+ }
+ return remaining
+}
+
+// filterByCompressExt filters all compressed files.
+func filterByCompressExt(files []logInfo, compress *[]logInfo, needCompress bool) {
+ if !needCompress {
+ return
+ }
+ for _, f := range files {
+ if !strings.HasSuffix(f.Name(), compressSuffix) {
+ *compress = append(*compress, f)
+ }
+ }
+}
+
+// compressFile compresses file src to dst, and removes src on success.
+func compressFile(src, dst string) (err error) {
+ f, err := os.Open(src)
+ if err != nil {
+ return fmt.Errorf("failed to open file: %v", err)
+ }
+ defer f.Close()
+
+ gzf, err := os.OpenFile(dst, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0666)
+ if err != nil {
+ return fmt.Errorf("failed to open compressed file: %v", err)
+ }
+ defer gzf.Close()
+
+ gz := gzip.NewWriter(gzf)
+ defer func() {
+ gz.Close()
+ if err != nil {
+ os.Remove(dst)
+ err = fmt.Errorf("failed to compress file: %v", err)
+ } else {
+ os.Remove(src)
+ }
+ }()
+
+ if _, err := io.Copy(gz, f); err != nil {
+ return err
+ }
+ return nil
+}
+
+// logInfo is an assistant struct which is used to return file name and last modified time.
+type logInfo struct {
+ timestamp time.Time
+ os.FileInfo
+}
+
+// byFormatTime sorts by time descending order.
+type byFormatTime []logInfo
+
+// Less checks whether the time of b[j] is early than the time of b[i].
+func (b byFormatTime) Less(i, j int) bool {
+ return b[i].timestamp.After(b[j].timestamp)
+}
+
+// Swap swaps b[i] and b[j].
+func (b byFormatTime) Swap(i, j int) {
+ b[i], b[j] = b[j], b[i]
+}
+
+// Len returns the length of list b.
+func (b byFormatTime) Len() int {
+ return len(b)
+}
diff --git a/log/rollwriter/roll_writer_options.go b/log/rollwriter/roll_writer_options.go
new file mode 100644
index 0000000..8830fe7
--- /dev/null
+++ b/log/rollwriter/roll_writer_options.go
@@ -0,0 +1,72 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package rollwriter
+
+// Options is the RollWriter call options.
+type Options struct {
+ // MaxSize is max size by byte of the log file.
+ MaxSize int64
+
+ // MaxBackups is the max number of log files.
+ MaxBackups int
+
+ // MaxAge is the max expire time by day of log files.
+ MaxAge int
+
+ // whether the log file should be compressed.
+ Compress bool
+
+ // TimeFormat is the time format to split log file by time.
+ TimeFormat string
+}
+
+// Option modifies the Options.
+type Option func(*Options)
+
+// WithMaxSize returns an Option which sets the max size(MB) of log files.
+func WithMaxSize(n int) Option {
+ return func(o *Options) {
+ o.MaxSize = int64(n) * 1024 * 1024
+ }
+}
+
+// WithMaxAge returns an Option which sets the max expire time(Day) of log files.
+func WithMaxAge(n int) Option {
+ return func(o *Options) {
+ o.MaxAge = n
+ }
+}
+
+// WithMaxBackups returns an Option which sets the max number of backup log files.
+func WithMaxBackups(n int) Option {
+ return func(o *Options) {
+ o.MaxBackups = n
+ }
+}
+
+// WithCompress returns an Option which sets whether log files should be compressed.
+func WithCompress(b bool) Option {
+ return func(o *Options) {
+ o.Compress = b
+ }
+}
+
+// WithRotationTime returns an Option which sets the time format(%Y%m%d) to roll logs.
+func WithRotationTime(s string) Option {
+ return func(o *Options) {
+ o.TimeFormat = s
+ }
+}
diff --git a/log/writer_factory.go b/log/writer_factory.go
new file mode 100644
index 0000000..b2bd073
--- /dev/null
+++ b/log/writer_factory.go
@@ -0,0 +1,116 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package log
+
+import (
+ "errors"
+ "path/filepath"
+
+ "github.com/apache/incubator-eventmesh/eventmesh-server-go/plugin"
+)
+
+var (
+ // DefaultConsoleWriterFactory is the default console output implementation.
+ DefaultConsoleWriterFactory = &ConsoleWriterFactory{}
+ // DefaultFileWriterFactory is the default file output implementation.
+ DefaultFileWriterFactory = &FileWriterFactory{}
+
+ writers = make(map[string]plugin.Plugin)
+)
+
+// RegisterWriter registers log output writer. Writer may have multiple implementations.
+func RegisterWriter(name string, writer plugin.Plugin) {
+ writers[name] = writer
+}
+
+// GetWriter gets log output writer, returns nil if not exist.
+func GetWriter(name string) plugin.Plugin {
+ return writers[name]
+}
+
+// ConsoleWriterFactory is the console writer instance.
+type ConsoleWriterFactory struct {
+}
+
+// Setup starts, loads and registers console output writer.
+func (f *ConsoleWriterFactory) Setup(name string, dec plugin.Decoder) error {
+ if dec == nil {
+ return errors.New("console writer decoder empty")
+ }
+ decoder, ok := dec.(*Decoder)
+ if !ok {
+ return errors.New("console writer log decoder type invalid")
+ }
+ cfg := &OutputConfig{}
+ if err := decoder.Decode(&cfg); err != nil {
+ return err
+ }
+ decoder.Core, decoder.ZapLevel = newConsoleCore(cfg)
+ return nil
+}
+
+// Type returns log file type.
+func (f *FileWriterFactory) Type() string {
+ return pluginType
+}
+
+// FileWriterFactory is the file writer instance Factory.
+type FileWriterFactory struct {
+}
+
+// Type returns log file type.
+func (f *ConsoleWriterFactory) Type() string {
+ return pluginType
+}
+
+// Setup starts, loads and register file output writer.
+func (f *FileWriterFactory) Setup(name string, dec plugin.Decoder) error {
+ if dec == nil {
+ return errors.New("file writer decoder empty")
+ }
+ decoder, ok := dec.(*Decoder)
+ if !ok {
+ return errors.New("file writer log decoder type invalid")
+ }
+ if err := f.setupConfig(decoder); err != nil {
+ return err
+ }
+ return nil
+}
+
+func (f *FileWriterFactory) setupConfig(decoder *Decoder) error {
+ cfg := &OutputConfig{}
+ if err := decoder.Decode(&cfg); err != nil {
+ return err
+ }
+ if cfg.WriteConfig.LogPath != "" {
+ cfg.WriteConfig.Filename = filepath.Join(cfg.WriteConfig.LogPath, cfg.WriteConfig.Filename)
+ }
+ if cfg.WriteConfig.RollType == "" {
+ cfg.WriteConfig.RollType = RollBySize
+ }
+ if cfg.WriteConfig.WriteMode == 0 {
+ // Use WriteFast as default mod.
+ // It has better performance, discards logs on full and avoid blocking service.
+ cfg.WriteConfig.WriteMode = WriteFast
+ }
+ core, level, err := newFileCore(cfg)
+ if err != nil {
+ return err
+ }
+ decoder.Core, decoder.ZapLevel = core, level
+ return nil
+}
diff --git a/log/zaplogger.go b/log/zaplogger.go
new file mode 100644
index 0000000..c16f20f
--- /dev/null
+++ b/log/zaplogger.go
@@ -0,0 +1,502 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package log
+
+import (
+ "fmt"
+ "os"
+ "strconv"
+ "time"
+
+ "github.com/apache/incubator-eventmesh/eventmesh-server-go/log/rollwriter"
+ "go.uber.org/zap"
+ "go.uber.org/zap/zapcore"
+)
+
+var defaultConfig = []OutputConfig{
+ {
+ Writer: "console",
+ Level: "debug",
+ Formatter: "console",
+ },
+}
+
+// Some ZapCore constants.
+const (
+ ConsoleZapCore = "console"
+ FileZapCore = "file"
+)
+
+// Levels is the map from string to zapcore.Level.
+var Levels = map[string]zapcore.Level{
+ "": zapcore.DebugLevel,
+ "debug": zapcore.DebugLevel,
+ "info": zapcore.InfoLevel,
+ "warn": zapcore.WarnLevel,
+ "error": zapcore.ErrorLevel,
+ "fatal": zapcore.FatalLevel,
+}
+
+var levelToZapLevel = map[Level]zapcore.Level{
+ LevelTrace: zapcore.DebugLevel,
+ LevelDebug: zapcore.DebugLevel,
+ LevelInfo: zapcore.InfoLevel,
+ LevelWarn: zapcore.WarnLevel,
+ LevelError: zapcore.ErrorLevel,
+ LevelFatal: zapcore.FatalLevel,
+}
+
+var zapLevelToLevel = map[zapcore.Level]Level{
+ zapcore.DebugLevel: LevelDebug,
+ zapcore.InfoLevel: LevelInfo,
+ zapcore.WarnLevel: LevelWarn,
+ zapcore.ErrorLevel: LevelError,
+ zapcore.FatalLevel: LevelFatal,
+}
+
+// NewZapLog creates a workflow default Logger from zap whose caller skip is set to 2.
+func NewZapLog(c Config) Logger {
+ return NewZapLogWithCallerSkip(c, 2)
+}
+
+// NewZapLogWithCallerSkip creates a workflow default Logger from zap.
+func NewZapLogWithCallerSkip(c Config, callerSkip int) Logger {
+ var (
+ cores []zapcore.Core
+ levels []zap.AtomicLevel
+ )
+ for _, o := range c {
+ writer := GetWriter(o.Writer)
+ if writer == nil {
+ panic("log: writer core: " + o.Writer + " no registered")
+ }
+ decoder := &Decoder{OutputConfig: &o}
+ if err := writer.Setup(o.Writer, decoder); err != nil {
+ panic("log: writer core: " + o.Writer + " setup fail: " + err.Error())
+ }
+ cores = append(cores, decoder.Core)
+ levels = append(levels, decoder.ZapLevel)
+ }
+ return &zapLog{
+ levels: levels,
+ logger: zap.New(
+ zapcore.NewTee(cores...),
+ zap.AddCallerSkip(callerSkip),
+ zap.AddCaller(),
+ ),
+ }
+}
+
+func newEncoder(c *OutputConfig) zapcore.Encoder {
+ encoderCfg := zapcore.EncoderConfig{
+ TimeKey: GetLogEncoderKey("T", c.FormatConfig.TimeKey),
+ LevelKey: GetLogEncoderKey("L", c.FormatConfig.LevelKey),
+ NameKey: GetLogEncoderKey("N", c.FormatConfig.NameKey),
+ CallerKey: GetLogEncoderKey("C", c.FormatConfig.CallerKey),
+ FunctionKey: GetLogEncoderKey(zapcore.OmitKey, c.FormatConfig.FunctionKey),
+ MessageKey: GetLogEncoderKey("M", c.FormatConfig.MessageKey),
+ StacktraceKey: GetLogEncoderKey("S", c.FormatConfig.StacktraceKey),
+ LineEnding: zapcore.DefaultLineEnding,
+ EncodeLevel: zapcore.CapitalLevelEncoder,
+ EncodeTime: NewTimeEncoder(c.FormatConfig.TimeFmt),
+ EncodeDuration: zapcore.StringDurationEncoder,
+ EncodeCaller: zapcore.ShortCallerEncoder,
+ }
+ switch c.Formatter {
+ case "console":
+ return zapcore.NewConsoleEncoder(encoderCfg)
+ case "json":
+ return zapcore.NewJSONEncoder(encoderCfg)
+ default:
+ return zapcore.NewConsoleEncoder(encoderCfg)
+ }
+}
+
+// GetLogEncoderKey gets user defined log output name, uses defKey if empty.
+func GetLogEncoderKey(defKey, key string) string {
+ if key == "" {
+ return defKey
+ }
+ return key
+}
+
+func newConsoleCore(c *OutputConfig) (zapcore.Core, zap.AtomicLevel) {
+ lvl := zap.NewAtomicLevelAt(Levels[c.Level])
+ return zapcore.NewCore(
+ newEncoder(c),
+ zapcore.Lock(os.Stdout),
+ lvl), lvl
+}
+
+func newFileCore(c *OutputConfig) (zapcore.Core, zap.AtomicLevel, error) {
+ opts := []rollwriter.Option{
+ rollwriter.WithMaxAge(c.WriteConfig.MaxAge),
+ rollwriter.WithMaxBackups(c.WriteConfig.MaxBackups),
+ rollwriter.WithCompress(c.WriteConfig.Compress),
+ rollwriter.WithMaxSize(c.WriteConfig.MaxSize),
+ }
+ // roll by time.
+ if c.WriteConfig.RollType != RollBySize {
+ opts = append(opts, rollwriter.WithRotationTime(c.WriteConfig.TimeUnit.Format()))
+ }
+ writer, err := rollwriter.NewRollWriter(c.WriteConfig.Filename, opts...)
+ if err != nil {
+ return nil, zap.AtomicLevel{}, err
+ }
+
+ // write mod.
+ var ws zapcore.WriteSyncer
+ if c.WriteConfig.WriteMode == WriteSync {
+ ws = zapcore.AddSync(writer)
+ } else {
+ dropLog := c.WriteConfig.WriteMode == WriteFast
+ ws = rollwriter.NewAsyncRollWriter(writer,
+ rollwriter.WithDropLog(dropLog),
+ )
+ }
+
+ // log level.
+ lvl := zap.NewAtomicLevelAt(Levels[c.Level])
+ return zapcore.NewCore(
+ newEncoder(c),
+ ws, lvl,
+ ), lvl, nil
+}
+
+// NewTimeEncoder creates a time format encoder.
+func NewTimeEncoder(format string) zapcore.TimeEncoder {
+ switch format {
+ case "":
+ return func(t time.Time, enc zapcore.PrimitiveArrayEncoder) {
+ enc.AppendByteString(DefaultTimeFormat(t))
+ }
+ case "seconds":
+ return zapcore.EpochTimeEncoder
+ case "milliseconds":
+ return zapcore.EpochMillisTimeEncoder
+ case "nanoseconds":
+ return zapcore.EpochNanosTimeEncoder
+ default:
+ return func(t time.Time, enc zapcore.PrimitiveArrayEncoder) {
+ enc.AppendString(CustomTimeFormat(t, format))
+ }
+ }
+}
+
+// CustomTimeFormat customize time format.
+func CustomTimeFormat(t time.Time, format string) string {
+ return t.Format(format)
+}
+
+// DefaultTimeFormat returns the default time format.
+func DefaultTimeFormat(t time.Time) []byte {
+ t = t.Local()
+ year, month, day := t.Date()
+ hour, minute, second := t.Clock()
+ micros := t.Nanosecond() / 1000
+
+ buf := make([]byte, 23)
+ buf[0] = byte((year/1000)%10) + '0'
+ buf[1] = byte((year/100)%10) + '0'
+ buf[2] = byte((year/10)%10) + '0'
+ buf[3] = byte(year%10) + '0'
+ buf[4] = '-'
+ buf[5] = byte((month)/10) + '0'
+ buf[6] = byte((month)%10) + '0'
+ buf[7] = '-'
+ buf[8] = byte((day)/10) + '0'
+ buf[9] = byte((day)%10) + '0'
+ buf[10] = ' '
+ buf[11] = byte((hour)/10) + '0'
+ buf[12] = byte((hour)%10) + '0'
+ buf[13] = ':'
+ buf[14] = byte((minute)/10) + '0'
+ buf[15] = byte((minute)%10) + '0'
+ buf[16] = ':'
+ buf[17] = byte((second)/10) + '0'
+ buf[18] = byte((second)%10) + '0'
+ buf[19] = '.'
+ buf[20] = byte((micros/100000)%10) + '0'
+ buf[21] = byte((micros/10000)%10) + '0'
+ buf[22] = byte((micros/1000)%10) + '0'
+ return buf
+}
+
+// ZapLogWrapper delegates zapLogger which was introduced in this
+// By ZapLogWrapper proxy, we can add a layer to the debug series function calls, so that the caller
+// information can be set correctly.
+type ZapLogWrapper struct {
+ l *zapLog
+}
+
+// GetLogger returns interval zapLog.
+func (z *ZapLogWrapper) GetLogger() Logger {
+ return z.l
+}
+
+// Trace logs to TRACE log. Arguments are handled in the manner of fmt.Print.
+func (z *ZapLogWrapper) Trace(args ...interface{}) {
+ z.l.Trace(args...)
+}
+
+// Tracef logs to TRACE log. Arguments are handled in the manner of fmt.Printf.
+func (z *ZapLogWrapper) Tracef(format string, args ...interface{}) {
+ z.l.Tracef(format, args...)
+}
+
+// Debug logs to DEBUG log. Arguments are handled in the manner of fmt.Print.
+func (z *ZapLogWrapper) Debug(args ...interface{}) {
+ z.l.Debug(args...)
+}
+
+// Debugf logs to DEBUG log. Arguments are handled in the manner of fmt.Printf.
+func (z *ZapLogWrapper) Debugf(format string, args ...interface{}) {
+ z.l.Debugf(format, args...)
+}
+
+// Info logs to INFO log. Arguments are handled in the manner of fmt.Print.
+func (z *ZapLogWrapper) Info(args ...interface{}) {
+ z.l.Info(args...)
+}
+
+// Infof logs to INFO log. Arguments are handled in the manner of fmt.Printf.
+func (z *ZapLogWrapper) Infof(format string, args ...interface{}) {
+ z.l.Infof(format, args...)
+}
+
+// Warn logs to WARNING log. Arguments are handled in the manner of fmt.Print.
+func (z *ZapLogWrapper) Warn(args ...interface{}) {
+ z.l.Warn(args...)
+}
+
+// Warnf logs to WARNING log. Arguments are handled in the manner of fmt.Printf.
+func (z *ZapLogWrapper) Warnf(format string, args ...interface{}) {
+ z.l.Warnf(format, args...)
+}
+
+// Error logs to ERROR log. Arguments are handled in the manner of fmt.Print.
+func (z *ZapLogWrapper) Error(args ...interface{}) {
+ z.l.Error(args...)
+}
+
+// Errorf logs to ERROR log. Arguments are handled in the manner of fmt.Printf.
+func (z *ZapLogWrapper) Errorf(format string, args ...interface{}) {
+ z.l.Errorf(format, args...)
+}
+
+// Fatal logs to FATAL log. Arguments are handled in the manner of fmt.Print.
+func (z *ZapLogWrapper) Fatal(args ...interface{}) {
+ z.l.Fatal(args...)
+}
+
+// Fatalf logs to FATAL log. Arguments are handled in the manner of fmt.Printf.
+func (z *ZapLogWrapper) Fatalf(format string, args ...interface{}) {
+ z.l.Fatalf(format, args...)
+}
+
+// Sync calls the zap logger's Sync method, and flushes any buffered log entries.
+// Applications should take care to call Sync before exiting.
+func (z *ZapLogWrapper) Sync() error {
+ return z.l.Sync()
+}
+
+// SetLevel set output log level.
+func (z *ZapLogWrapper) SetLevel(output string, level Level) {
+ z.l.SetLevel(output, level)
+}
+
+// GetLevel gets output log level.
+func (z *ZapLogWrapper) GetLevel(output string) Level {
+ return z.l.GetLevel(output)
+}
+
+// WithFields set some user defined data to logs, such as uid, imei, etc.
+// Use this function at the beginning of each request. The returned new Logger should be used to
+// print logs.
+// Fields must be paired.
+// Deprecated: use With instead.
+func (z *ZapLogWrapper) WithFields(fields ...string) Logger {
+ return z.l.WithFields(fields...)
+}
+
+// With add user defined fields to Logger. Fields support multiple values.
+func (z *ZapLogWrapper) With(fields ...Field) Logger {
+ return z.l.With(fields...)
+}
+
+// zapLog is a Logger implementation based on zaplogger.
+type zapLog struct {
+ levels []zap.AtomicLevel
+ logger *zap.Logger
+}
+
+// WithFields set some user defined data to logs, such as uid, imei, etc.
+// Use this function at the beginning of each request. The returned new Logger should be used to
+// print logs.
+// Fields must be paired.
+// Deprecated: use With instead.
+func (l *zapLog) WithFields(fields ...string) Logger {
+ zapFields := make([]zap.Field, len(fields)/2)
+ for i := range zapFields {
+ zapFields[i] = zap.String(fields[2*i], fields[2*i+1])
+ }
+
+ // By ZapLogWrapper proxy, we can add a layer to the debug series function calls, so that the
+ // caller information can be set correctly.
+ return &ZapLogWrapper{
+ l: &zapLog{
+ levels: l.levels,
+ logger: l.logger.With(zapFields...)}}
+}
+
+// With add user defined fields to Logger. Fields support multiple values.
+func (l *zapLog) With(fields ...Field) Logger {
+ zapFields := make([]zap.Field, len(fields))
+ for i := range fields {
+ zapFields[i] = zap.Any(fields[i].Key, fields[i].Value)
+ }
+
+ // By ZapLogWrapper proxy, we can add a layer to the debug series function calls, so that the
+ // caller information can be set correctly.
+ return &ZapLogWrapper{
+ l: &zapLog{
+ levels: l.levels,
+ logger: l.logger.With(zapFields...)}}
+}
+
+func getLogMsg(args ...interface{}) string {
+ msg := fmt.Sprint(args...)
+ return msg
+}
+
+func getLogMsgf(format string, args ...interface{}) string {
+ msg := fmt.Sprintf(format, args...)
+ return msg
+}
+
+// Trace logs to TRACE log. Arguments are handled in the manner of fmt.Print.
+func (l *zapLog) Trace(args ...interface{}) {
+ if l.logger.Core().Enabled(zapcore.DebugLevel) {
+ l.logger.Debug(getLogMsg(args...))
+ }
+}
+
+// Tracef logs to TRACE log. Arguments are handled in the manner of fmt.Printf.
+func (l *zapLog) Tracef(format string, args ...interface{}) {
+ if l.logger.Core().Enabled(zapcore.DebugLevel) {
+ l.logger.Debug(getLogMsgf(format, args...))
+ }
+}
+
+// Debug logs to DEBUG log. Arguments are handled in the manner of fmt.Print.
+func (l *zapLog) Debug(args ...interface{}) {
+ if l.logger.Core().Enabled(zapcore.DebugLevel) {
+ l.logger.Debug(getLogMsg(args...))
+ }
+}
+
+// Debugf logs to DEBUG log. Arguments are handled in the manner of fmt.Printf.
+func (l *zapLog) Debugf(format string, args ...interface{}) {
+ if l.logger.Core().Enabled(zapcore.DebugLevel) {
+ l.logger.Debug(getLogMsgf(format, args...))
+ }
+}
+
+// Info logs to INFO log. Arguments are handled in the manner of fmt.Print.
+func (l *zapLog) Info(args ...interface{}) {
+ if l.logger.Core().Enabled(zapcore.InfoLevel) {
+ l.logger.Info(getLogMsg(args...))
+ }
+}
+
+// Infof logs to INFO log. Arguments are handled in the manner of fmt.Printf.
+func (l *zapLog) Infof(format string, args ...interface{}) {
+ if l.logger.Core().Enabled(zapcore.InfoLevel) {
+ l.logger.Info(getLogMsgf(format, args...))
+ }
+}
+
+// Warn logs to WARNING log. Arguments are handled in the manner of fmt.Print.
+func (l *zapLog) Warn(args ...interface{}) {
+ if l.logger.Core().Enabled(zapcore.WarnLevel) {
+ l.logger.Warn(getLogMsg(args...))
+ }
+}
+
+// Warnf logs to WARNING log. Arguments are handled in the manner of fmt.Printf.
+func (l *zapLog) Warnf(format string, args ...interface{}) {
+ if l.logger.Core().Enabled(zapcore.WarnLevel) {
+ l.logger.Warn(getLogMsgf(format, args...))
+ }
+}
+
+// Error logs to ERROR log. Arguments are handled in the manner of fmt.Print.
+func (l *zapLog) Error(args ...interface{}) {
+ if l.logger.Core().Enabled(zapcore.ErrorLevel) {
+ l.logger.Error(getLogMsg(args...))
+ }
+}
+
+// Errorf logs to ERROR log. Arguments are handled in the manner of fmt.Printf.
+func (l *zapLog) Errorf(format string, args ...interface{}) {
+ if l.logger.Core().Enabled(zapcore.ErrorLevel) {
+ l.logger.Error(getLogMsgf(format, args...))
+ }
+}
+
+// Fatal logs to FATAL log. Arguments are handled in the manner of fmt.Print.
+func (l *zapLog) Fatal(args ...interface{}) {
+ if l.logger.Core().Enabled(zapcore.FatalLevel) {
+ l.logger.Fatal(getLogMsg(args...))
+ }
+}
+
+// Fatalf logs to FATAL log. Arguments are handled in the manner of fmt.Printf.
+func (l *zapLog) Fatalf(format string, args ...interface{}) {
+ if l.logger.Core().Enabled(zapcore.FatalLevel) {
+ l.logger.Fatal(getLogMsgf(format, args...))
+ }
+}
+
+// Sync calls the zap logger's Sync method, and flushes any buffered log entries.
+// Applications should take care to call Sync before exiting.
+func (l *zapLog) Sync() error {
+ return l.logger.Sync()
+}
+
+// SetLevel sets output log level.
+func (l *zapLog) SetLevel(output string, level Level) {
+ i, e := strconv.Atoi(output)
+ if e != nil {
+ return
+ }
+ if i < 0 || i >= len(l.levels) {
+ return
+ }
+ l.levels[i].SetLevel(levelToZapLevel[level])
+}
+
+// GetLevel gets output log level.
+func (l *zapLog) GetLevel(output string) Level {
+ i, e := strconv.Atoi(output)
+ if e != nil {
+ return LevelDebug
+ }
+ if i < 0 || i >= len(l.levels) {
+ return LevelDebug
+ }
+ return zapLevelToLevel[l.levels[i].Level()]
+}
diff --git a/pkg/README.md b/pkg/README.md
new file mode 100644
index 0000000..96236ab
--- /dev/null
+++ b/pkg/README.md
@@ -0,0 +1 @@
+external used code
\ No newline at end of file
diff --git a/pkg/common/protocol/grpc/keys.go b/pkg/common/protocol/grpc/keys.go
new file mode 100644
index 0000000..b627a25
--- /dev/null
+++ b/pkg/common/protocol/grpc/keys.go
@@ -0,0 +1,65 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package grpc
+
+var (
+ ENV = "env"
+ IDC = "idc"
+ SYS = "sys"
+ PID = "pid"
+ IP = "ip"
+ USERNAME = "username"
+ PASSWD = "passwd"
+ LANGUAGE = "language"
+ PROTOCOL_TYPE = "protocoltype"
+ PROTOCOL_VERSION = "protocolversion"
+ PROTOCOL_DESC = "protocoldesc"
+ SEQ_NUM = "seqnum"
+ UNIQUE_ID = "uniqueid"
+ TTL = "ttl"
+ PRODUCERGROUP = "producergroup"
+ TAG = "tag"
+ CONTENT_TYPE = "contenttype"
+)
+
+// request code
+var (
+ HTTP_PUSH_CLIENT_ASYNC = "105"
+)
+
+// protocol key
+var (
+ REQUEST_CODE = "code"
+ Version = "version"
+)
+
+// EventMeshInstanceKey
+var (
+ EVENTMESHCLUSTER = "eventmeshcluster"
+ EVENTMESHIP = "eventmeship"
+ EVENTMESHENV = "eventmeshenv"
+ EVENTMESHIDC = "eventmeshidc"
+)
+
+// PushMessageRequestBody
+var (
+ RANDOMNO = "randomNo"
+ TOPIC = "topic"
+ BIZSEQNO = "bizseqno"
+ UNIQUEID = "uniqueId"
+ CONTENT = "content"
+ EXTFIELDS = "extFields"
+)
diff --git a/pkg/common/protocol/grpc/message.go b/pkg/common/protocol/grpc/message.go
new file mode 100644
index 0000000..ce1029f
--- /dev/null
+++ b/pkg/common/protocol/grpc/message.go
@@ -0,0 +1,26 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package grpc
+
+import "github.com/apache/incubator-eventmesh/eventmesh-server-go/runtime/proto/pb"
+
+type SimpleMessageWrapper struct {
+ SimpleMessage *pb.SimpleMessage `json:"simpleMessage"`
+}
+
+type BatchMessageWrapper struct {
+ BatchMessage *pb.BatchMessage `json:"batchMessage"`
+}
diff --git a/pkg/common/protocol/grpc/status_code.go b/pkg/common/protocol/grpc/status_code.go
new file mode 100644
index 0000000..e6b730f
--- /dev/null
+++ b/pkg/common/protocol/grpc/status_code.go
@@ -0,0 +1,68 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package grpc
+
+import (
+ "fmt"
+ jsoniter "github.com/json-iterator/go"
+)
+
+type StatusCode struct {
+ RetCode string `json:"respCode"`
+ ErrMsg string `json:"respMsg"`
+}
+
+func (s *StatusCode) ToError() error {
+ return fmt.Errorf("code:%v, msg:%v", s.RetCode, s.ErrMsg)
+}
+
+func (s *StatusCode) ToJSONString() string {
+ str, _ := jsoniter.MarshalToString(s)
+ return str
+}
+
+var (
+ SUCCESS = &StatusCode{"0", "success"}
+ OVERLOAD = &StatusCode{"1", "eventMesh overload, try later, "}
+ EVENTMESH_REQUESTCODE_INVALID = &StatusCode{"2", "requestCode can't be null, or must be number, "}
+ EVENTMESH_SEND_SYNC_MSG_ERR = &StatusCode{"3", "eventMesh send rr msg err, "}
+ EVENTMESH_WAITING_RR_MSG_ERR = &StatusCode{"4", "eventMesh waiting rr msg err, "}
+ EVENTMESH_PROTOCOL_HEADER_ERR = &StatusCode{"6", "eventMesh protocol[header] err, "}
+ EVENTMESH_PROTOCOL_BODY_ERR = &StatusCode{"7", "eventMesh protocol[body] err, "}
+ EVENTMESH_STOP = &StatusCode{"8", "eventMesh will stop or had stopped, "}
+ EVENTMESH_REJECT_BY_PROCESSOR_ERROR = &StatusCode{"9", "eventMesh reject by processor error, "}
+ EVENTMESH_BATCH_PUBLISH_ERR = &StatusCode{"10", "eventMesh batch publish messages error, "}
+ EVENTMESH_SEND_BATCHLOG_MSG_ERR = &StatusCode{"17", "eventMesh send batchlog msg err, "}
+ EVENTMESH_BATCH_SPEED_OVER_LIMIT_ERR = &StatusCode{"11", "eventMesh batch msg speed over the limit, "}
+ EVENTMESH_PACKAGE_MSG_ERR = &StatusCode{"12", "eventMesh package msg err, "}
+ EVENTMESH_GROUP_PRODUCER_STOPED_ERR = &StatusCode{"13", "eventMesh group producer stopped, "}
+ EVENTMESH_SEND_ASYNC_MSG_ERR = &StatusCode{"14", "eventMesh send async msg err, "}
+ EVENTMESH_REPLY_MSG_ERR = &StatusCode{"15", "eventMesh reply msg err, "}
+ EVENTMESH_RUNTIME_ERR = &StatusCode{"16", "eventMesh runtime err, "}
+ EVENTMESH_SUBSCRIBE_ERR = &StatusCode{"17", "eventMesh subscribe err"}
+ EVENTMESH_UNSUBSCRIBE_ERR = &StatusCode{"18", "eventMesh unsubscribe err"}
+ EVENTMESH_HEARTBEAT_ERR = &StatusCode{"19", "eventMesh heartbeat err"}
+ EVENTMESH_ACL_ERR = &StatusCode{"20", "eventMesh acl err"}
+ EVENTMESH_SEND_MESSAGE_SPEED_OVER_LIMIT_ERR = &StatusCode{"21", "eventMesh send message speed over the limit err."}
+ EVENTMESH_REQUEST_REPLY_MSG_ERR = &StatusCode{"22", "eventMesh request reply msg err, "}
+ EVENTMESH_Plugin_NotFound_ERR = &StatusCode{"23", "eventMesh plugin not found err"}
+ EVENTMESH_Transfer_Protocol_ERR = &StatusCode{"24", "eventMesh plugin build event message err"}
+ EVENTMESH_Producer_Group_NotFound_ERR = &StatusCode{"25", "eventMesh producer not found err build event message err"}
+ EVENTMESH_Subscribe_Register_ERR = &StatusCode{"26", "eventMesh subscribe register err"}
+ EVENTMESH_Consumer_NotFound_ERR = &StatusCode{"27", "eventMesh consumer not found err build event message err"}
+ EVENTMESH_Restart_Consumer_ERR = &StatusCode{"28", "eventMesh restart consumer err"}
+ EVENTMESH_Heartbeat_Protocol_ERR = &StatusCode{"29", "eventMesh heartbeat but protocol not sub err"}
+)
diff --git a/pkg/common/protocol/tcp/pck.go b/pkg/common/protocol/tcp/pck.go
new file mode 100644
index 0000000..b13b02a
--- /dev/null
+++ b/pkg/common/protocol/tcp/pck.go
@@ -0,0 +1,97 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package tcp
+
+type Package struct {
+ Header Header
+ Body interface{}
+}
+
+type Header struct {
+ CMD Command `json:"cmd"`
+ Code int `json:"code"`
+ Desc string `json:"desc"`
+ Seq string `json:"seq"`
+}
+
+type Command int
+
+const (
+ HEARTBEAT_REQUEST Command = 0 //client send heartbeat packet to server
+
+ HEARTBEAT_RESPONSE Command = 1 //server response heartbeat packet of client
+
+ HELLO_REQUEST Command = 2 //client send handshake request to server
+
+ HELLO_RESPONSE Command = 3 //server response handshake request of client
+
+ //disconnection
+ CLIENT_GOODBYE_REQUEST Command = 4 //Notify server when client actively disconnects
+ CLIENT_GOODBYE_RESPONSE Command = 5
+ SERVER_GOODBYE_REQUEST Command = 6 //Notify client when server actively disconnects
+ SERVER_GOODBYE_RESPONSE Command = 7 //Client replies to server's active disconnection notification
+
+ //subscription management
+ SUBSCRIBE_REQUEST Command = 8 //Subscription request sent by client to server
+ SUBSCRIBE_RESPONSE Command = 9 //Server replies to client's subscription request
+ UNSUBSCRIBE_REQUEST Command = 10 //Unsubscribe request from client to server
+ UNSUBSCRIBE_RESPONSE Command = 11 //Server replies to client's unsubscribe request
+
+ //monitor
+ LISTEN_REQUEST Command = 12 //Request from client to server to start topic listening
+ LISTEN_RESPONSE Command = 13 //The server replies to the client's listening request
+
+ //RR
+ REQUEST_TO_SERVER Command = 14 //The client sends the RR request to the server
+ REQUEST_TO_CLIENT Command = 15 //The server pushes the RR request to the client
+ REQUEST_TO_CLIENT_ACK Command = 16 //After receiving RR request, the client sends ACK to the server
+ RESPONSE_TO_SERVER Command = 17 //The client sends the RR packet back to the server
+ RESPONSE_TO_CLIENT Command = 18 //The server pushes the RR packet back to the client
+ RESPONSE_TO_CLIENT_ACK Command = 19 //After receiving the return packet, the client sends ACK to the server
+
+ //Asynchronous events
+ ASYNC_MESSAGE_TO_SERVER Command = 20 //The client sends asynchronous events to the server
+ ASYNC_MESSAGE_TO_SERVER_ACK Command = 21 //After receiving the asynchronous event, the server sends ack to the client
+ ASYNC_MESSAGE_TO_CLIENT Command = 22 //The server pushes asynchronous events to the client
+ ASYNC_MESSAGE_TO_CLIENT_ACK Command = 23 //After the client receives the asynchronous event, the ACK is sent to the server
+
+ //radio broadcast
+ BROADCAST_MESSAGE_TO_SERVER Command = 24 //The client sends the broadcast message to the server
+ BROADCAST_MESSAGE_TO_SERVER_ACK Command = 25 //After receiving the broadcast message, the server sends ACK to the client
+ BROADCAST_MESSAGE_TO_CLIENT Command = 26 //The server pushes the broadcast message to the client
+ BROADCAST_MESSAGE_TO_CLIENT_ACK Command = 27 //After the client receives the broadcast message, the ACK is sent to the server
+
+ //Log reporting
+ SYS_LOG_TO_LOGSERVER Command = 28 //Business log reporting
+
+ //RMB tracking log reporting
+ TRACE_LOG_TO_LOGSERVER Command = 29 //RMB tracking log reporting
+
+ //Redirecting instruction
+ REDIRECT_TO_CLIENT Command = 30 //The server pushes the redirection instruction to the client
+
+ //service register
+ REGISTER_REQUEST Command = 31 //Client sends registration request to server
+ REGISTER_RESPONSE Command = 32 //The server sends the registration result to the client
+
+ //service unregister
+ UNREGISTER_REQUEST Command = 33 //The client sends a de registration request to the server
+ UNREGISTER_RESPONSE Command = 34 //The server will register the result to the client
+
+ //The client asks which EventMesh to recommend
+ RECOMMEND_REQUEST Command = 35 //Client sends recommendation request to server
+ RECOMMEND_RESPONSE Command = 36 //The server will recommend the results to the client
+)
diff --git a/pkg/naming/registry/instance.go b/pkg/naming/registry/instance.go
new file mode 100644
index 0000000..8499f7a
--- /dev/null
+++ b/pkg/naming/registry/instance.go
@@ -0,0 +1,34 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package registry
+
+import (
+ "fmt"
+)
+
+// Instance is the information of a instance.
+type Instance struct {
+ ServiceName string
+ Address string
+ Weight int
+ Clusters string
+ Metadata map[string]string
+}
+
+// String returns an abbreviation information of instance.
+func (n *Instance) String() string {
+ return fmt.Sprintf("service:%s, addr:%s", n.ServiceName, n.Address)
+}
diff --git a/pkg/naming/registry/registry.go b/pkg/naming/registry/registry.go
new file mode 100644
index 0000000..6faf8a0
--- /dev/null
+++ b/pkg/naming/registry/registry.go
@@ -0,0 +1,72 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Package registry registers servers. A server report itself on start.
+package registry
+
+import (
+ "errors"
+ "sync"
+)
+
+// ErrNotImplement is the not implemented error.
+var ErrNotImplement = errors.New("not implement")
+
+// DefaultRegistry is the default registry.
+var DefaultRegistry Registry = &NoopRegistry{}
+
+// SetDefaultRegistry sets the default registry.
+func SetDefaultRegistry(r Registry) {
+ DefaultRegistry = r
+}
+
+// Registry is the interface that defines a register.
+type Registry interface {
+ Register(service string) error
+ Deregister(service string) error
+}
+
+var (
+ registries = make(map[string]Registry)
+ lock = sync.RWMutex{}
+)
+
+// Register registers a named registry. Each service has its own registry.
+func Register(name string, s Registry) {
+ lock.Lock()
+ registries[name] = s
+ lock.Unlock()
+}
+
+// Get gets a named registry.
+func Get(name string) Registry {
+ lock.RLock()
+ r := registries[name]
+ lock.RUnlock()
+ return r
+}
+
+// NoopRegistry is the noop registry.
+type NoopRegistry struct{}
+
+// Register always returns ErrNotImplement.
+func (noop *NoopRegistry) Register(service string) error {
+ return ErrNotImplement
+}
+
+// Deregister always return ErrNotImplement.
+func (noop *NoopRegistry) Deregister(service string) error {
+ return ErrNotImplement
+}
diff --git a/pkg/naming/selector/selector.go b/pkg/naming/selector/selector.go
new file mode 100644
index 0000000..3f99f89
--- /dev/null
+++ b/pkg/naming/selector/selector.go
@@ -0,0 +1,43 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package selector
+
+import "github.com/apache/incubator-eventmesh/eventmesh-server-go/pkg/naming/registry"
+
+// Selector is the interface that defines the selector.
+type Selector interface {
+ // Select gets a backend instance by service name.
+ Select(serviceName string) (*registry.Instance, error)
+}
+
+var (
+ selectors = make(map[string]Selector)
+)
+
+// Register registers a named Selector, such as l5, cmlb and tseer.
+func Register(name string, s Selector) {
+ selectors[name] = s
+}
+
+// Get gets a named Selector.
+func Get(name string) Selector {
+ s := selectors[name]
+ return s
+}
+
+func unregisterForTesting(name string) {
+ delete(selectors, name)
+}
diff --git a/pkg/util/id.go b/pkg/util/id.go
new file mode 100644
index 0000000..5a3833b
--- /dev/null
+++ b/pkg/util/id.go
@@ -0,0 +1,39 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package util
+
+import (
+ "fmt"
+ "github.com/apache/incubator-eventmesh/eventmesh-server-go/pkg/version"
+ "strings"
+)
+
+func BuildMeshTcpClientID(sys, purpose, cluster string) string {
+ return fmt.Sprintf("%v-%v-%v-%v-%v",
+ strings.TrimSpace(sys),
+ strings.TrimSpace(purpose),
+ strings.TrimSpace(cluster),
+ strings.TrimSpace(version.Current),
+ strings.TrimSpace(PID()))
+}
+
+func BuildMeshClientID(group, cluster string) string {
+ return fmt.Sprintf("%v-(%v)-%v-%v",
+ strings.TrimSpace(group),
+ strings.TrimSpace(cluster),
+ strings.TrimSpace(version.Current),
+ strings.TrimSpace(PID()))
+}
diff --git a/pkg/util/id_test.go b/pkg/util/id_test.go
new file mode 100644
index 0000000..61d15a6
--- /dev/null
+++ b/pkg/util/id_test.go
@@ -0,0 +1,94 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package util
+
+import (
+ "github.com/apache/incubator-eventmesh/eventmesh-server-go/pkg/version"
+ "github.com/stretchr/testify/assert"
+ "testing"
+)
+
+func TestBuildMeshClientID(t *testing.T) {
+ type args struct {
+ group string
+ cluster string
+ }
+ tests := []struct {
+ name string
+ args args
+ want string
+ }{
+ {
+ name: "build client id",
+ args: args{
+ group: "test-group",
+ cluster: "idc",
+ },
+ want: "test-group-(idc)-" + version.Current + "-" + PID(),
+ },
+ {
+ name: "build client id with upper",
+ args: args{
+ group: "Test-group",
+ cluster: "IDC",
+ },
+ want: "Test-group-(IDC)-" + version.Current + "-" + PID(),
+ },
+ {
+ name: "build client id wit space",
+ args: args{
+ group: " Test-group ",
+ cluster: "idc ",
+ },
+ want: "Test-group-(idc)-" + version.Current + "-" + PID(),
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ if got := BuildMeshClientID(tt.args.group, tt.args.cluster); got != tt.want {
+ t.Errorf("BuildMeshClientID() = %v, want %v", got, tt.want)
+ }
+ })
+ }
+}
+
+func TestBuildMeshTcpClientID(t *testing.T) {
+ type args struct {
+ sys string
+ purpose string
+ cluster string
+ }
+ tests := []struct {
+ name string
+ args args
+ want string
+ }{
+ {
+ name: "uniq mesh tcp client id",
+ args: args{
+ sys: "1234",
+ purpose: "test",
+ cluster: "c1",
+ },
+ want: "1234-test-c1-" + version.Current + "-" + PID(),
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ assert.Equalf(t, tt.want, BuildMeshTcpClientID(tt.args.sys, tt.args.purpose, tt.args.cluster), "BuildMeshTcpClientID(%v, %v, %v)", tt.args.sys, tt.args.purpose, tt.args.cluster)
+ })
+ }
+}
diff --git a/pkg/util/ip.go b/pkg/util/ip.go
new file mode 100644
index 0000000..243bb60
--- /dev/null
+++ b/pkg/util/ip.go
@@ -0,0 +1,43 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package util
+
+import (
+ "errors"
+ "net"
+)
+
+var ip string
+
+func init() {
+ addresses, err := net.InterfaceAddrs()
+ if err != nil {
+ panic(err)
+ }
+ for _, address := range addresses {
+ if ipnet, ok := address.(*net.IPNet); ok && !ipnet.IP.IsLoopback() {
+ if ipnet.IP.To4() != nil {
+ ip = ipnet.IP.String()
+ return
+ }
+ }
+ }
+ panic(errors.New("can not find the client ip address"))
+}
+
+func GetIP() string {
+ return ip
+}
diff --git a/pkg/util/pid.go b/pkg/util/pid.go
new file mode 100644
index 0000000..97208d9
--- /dev/null
+++ b/pkg/util/pid.go
@@ -0,0 +1,32 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package util
+
+import (
+ "fmt"
+ "os"
+)
+
+var _pid string
+
+func init() {
+ _pid = fmt.Sprintf("%v", os.Getpid())
+}
+
+// PID return the current process id
+func PID() string {
+ return _pid
+}
diff --git a/pkg/util/pid_test.go b/pkg/util/pid_test.go
new file mode 100644
index 0000000..42483d9
--- /dev/null
+++ b/pkg/util/pid_test.go
@@ -0,0 +1,37 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package util
+
+import (
+ "github.com/stretchr/testify/assert"
+ "testing"
+)
+
+func TestPID(t *testing.T) {
+ tests := []struct {
+ name string
+ }{
+ {
+ name: "pid",
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ got := PID()
+ assert.NotEmpty(t, got)
+ })
+ }
+}
diff --git a/pkg/version/version.go b/pkg/version/version.go
new file mode 100644
index 0000000..2a7828f
--- /dev/null
+++ b/pkg/version/version.go
@@ -0,0 +1,20 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package version
+
+var (
+ Current = "v0.0.1"
+)
diff --git a/plugin/connector/connector.go b/plugin/connector/connector.go
new file mode 100644
index 0000000..46d3b53
--- /dev/null
+++ b/plugin/connector/connector.go
@@ -0,0 +1,122 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package connector
+
+import (
+ "context"
+ "time"
+
+ ce "github.com/cloudevents/sdk-go/v2"
+
+ "github.com/apache/incubator-eventmesh/eventmesh-server-go/plugin"
+)
+
+const (
+ PluginType = "connector"
+)
+
+// Factory plugin factory of consumer/producer/resource
+type Factory interface {
+ plugin.Plugin
+
+ GetConsumer() (Consumer, error)
+ GetProducer() (Producer, error)
+ GetResource() (Resource, error)
+}
+
+// EventMeshAction commit action of message consume
+type EventMeshAction uint
+
+const (
+ CommitMessage EventMeshAction = iota
+ ReconsumeLater
+ ManualAck
+)
+
+// Consumer interface of consumer
+// all the consumers implement this interface should implement a corresponding factory and do plugin registration first.
+type Consumer interface {
+ LifeCycle
+
+ InitConsumer(properties map[string]string) error
+ UpdateOffset(ctx context.Context, events []*ce.Event) error
+ Subscribe(topicName string) error
+ Unsubscribe(topicName string) error
+ RegisterEventListener(listener *EventListener)
+}
+
+// Producer interface of producer
+// all the producers implement this interface should implement a corresponding factory and do plugin registration first.
+type Producer interface {
+ LifeCycle
+
+ InitProducer(properties map[string]string) error
+ Publish(ctx context.Context, event *ce.Event, callback *SendCallback) error
+ SendOneway(ctx context.Context, event *ce.Event) error
+ Request(ctx context.Context, event *ce.Event, callback *RequestReplyCallback, timeout time.Duration) error
+ Reply(ctx context.Context, event *ce.Event, callback *SendCallback) error
+ CheckTopicExist(topicName string) (bool, error)
+ SetExtFields() error
+}
+
+// Resource interface of resource service
+// all the resources implement this interface should implement a corresponding factory and do plugin registration first.
+type Resource interface {
+ Init() error
+ Release() error
+}
+
+// LifeCycle general life cycle interface for all connectors
+type LifeCycle interface {
+ IsStarted() bool
+ IsClosed() bool
+ Start() error
+ Shutdown() error
+}
+
+// SendCallback send callback handler of function Publish
+type SendCallback struct {
+ OnSuccess func(result *SendResult)
+ OnError func(result *ErrorResult)
+}
+
+// RequestReplyCallback request/reply callback handler of function Request and Reply
+type RequestReplyCallback struct {
+ OnSuccess func(event *ce.Event)
+ OnError func(result *ErrorResult)
+}
+
+type SendResult struct {
+ MessageId string
+ Topic string
+ Err error
+}
+
+type ErrorResult struct {
+ Topic string
+ Err error
+}
+
+// EventListener message consume handler
+type EventListener struct {
+ Consume ConsumeFunc
+}
+
+// CommitFunc user can commit message through this function in the consuming logic
+type CommitFunc func(action EventMeshAction) error
+
+// ConsumeFunc custom message consuming logic
+type ConsumeFunc func(event *ce.Event, commitFunc CommitFunc) error
diff --git a/plugin/connector/rocketmq/client/rocketmq_consumer.go b/plugin/connector/rocketmq/client/rocketmq_consumer.go
new file mode 100644
index 0000000..2cc867b
--- /dev/null
+++ b/plugin/connector/rocketmq/client/rocketmq_consumer.go
@@ -0,0 +1,159 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package client
+
+import (
+ "context"
+ "errors"
+ "fmt"
+ "github.com/apache/incubator-eventmesh/eventmesh-server-go/plugin/connector/rocketmq/constants"
+ "github.com/apache/rocketmq-client-go/v2"
+ "github.com/apache/rocketmq-client-go/v2/consumer"
+ "github.com/apache/rocketmq-client-go/v2/primitive"
+ "strconv"
+ "strings"
+)
+
+type SubscribeFunc func(context.Context, ...*primitive.MessageExt) (consumer.ConsumeResult, error)
+
+// RocketMQConsumer RocketMQ consumer interface
+type RocketMQConsumer interface {
+ Start() error
+ Shutdown() error
+ Subscribe(topic string, selector consumer.MessageSelector, f SubscribeFunc) error
+ Unsubscribe(topic string) error
+ Suspend()
+ Resume()
+ IsBroadCasting() bool
+}
+
+// RocketMQConsumerWrapper consumer wrapper of RocketMQ client
+type RocketMQConsumerWrapper struct {
+ options []consumer.Option
+ consumer rocketmq.PushConsumer
+ messageModel consumer.MessageModel
+}
+
+// NewRocketMQConsumerWrapper get a consumer wrapper of RocketMQ client
+func NewRocketMQConsumerWrapper(properties map[string]string) (RocketMQConsumer, error) {
+ consumerWrapper := &RocketMQConsumerWrapper{messageModel: consumer.Clustering}
+
+ options, err := consumerWrapper.getConsumerOptionsFromProperties(properties)
+ if err != nil {
+ return nil, err
+ }
+
+ rocketMQConsumer, err := rocketmq.NewPushConsumer(options...)
+ if err != nil {
+ return nil, err
+ }
+ consumerWrapper.consumer = rocketMQConsumer
+
+ return consumerWrapper, nil
+}
+
+// Start wrapper start function
+func (r *RocketMQConsumerWrapper) Start() error {
+ return r.consumer.Start()
+}
+
+// Shutdown wrapper shutdown function
+func (r *RocketMQConsumerWrapper) Shutdown() error {
+ return r.consumer.Shutdown()
+}
+
+// Subscribe wrapper subscribe function
+func (r *RocketMQConsumerWrapper) Subscribe(topic string, selector consumer.MessageSelector, f SubscribeFunc) error {
+ return r.consumer.Subscribe(topic, selector, f)
+}
+
+// Unsubscribe wrapper unsubscribe function
+func (r *RocketMQConsumerWrapper) Unsubscribe(topic string) error {
+ return r.consumer.Unsubscribe(topic)
+}
+
+// Suspend wrapper suspend function
+func (r *RocketMQConsumerWrapper) Suspend() {
+ r.consumer.Suspend()
+}
+
+// Resume wrapper resume function
+func (r *RocketMQConsumerWrapper) Resume() {
+ r.consumer.Resume()
+}
+
+// IsBroadCasting check if consumer mode is broadcasting
+func (r *RocketMQConsumerWrapper) IsBroadCasting() bool {
+ return r.messageModel == consumer.BroadCasting
+}
+
+// getConsumerOptionsFromProperties convert properties map to client options
+func (r *RocketMQConsumerWrapper) getConsumerOptionsFromProperties(properties map[string]string) ([]consumer.Option, error) {
+ clientConfig, err := getClientConfigFromProperties(properties)
+ if clientConfig == nil {
+ return nil, err
+ }
+ options := make([]consumer.Option, 0)
+
+ accessPoints := clientConfig.AccessPoints
+ if len(accessPoints) == 0 {
+ return nil, errors.New("fail to parse rocketmq consumer config, invalid access points")
+ }
+
+ // name server address
+ options = append(options, consumer.WithNameServer(strings.Split(accessPoints, ",")))
+
+ // max reconsume times
+ if len(clientConfig.MaxReconsumeTimes) != 0 {
+ maxReconsumeTimes, err := strconv.ParseInt(clientConfig.MaxReconsumeTimes, 10, 32)
+ if err == nil {
+ options = append(options, consumer.WithMaxReconsumeTimes(int32(maxReconsumeTimes)))
+ }
+ }
+
+ // consume message model
+ isBroadCasting := false
+ if len(clientConfig.MessageModel) != 0 {
+ switch clientConfig.MessageModel {
+ case consumer.BroadCasting.String():
+ options = append(options, consumer.WithConsumerModel(consumer.BroadCasting))
+ isBroadCasting = true
+ r.messageModel = consumer.BroadCasting
+ default:
+ options = append(options, consumer.WithConsumerModel(consumer.Clustering))
+ r.messageModel = consumer.Clustering
+ }
+ }
+
+ // consumer group
+ if len(clientConfig.ConsumerGroup) == 0 {
+ return nil, errors.New("fail to create rocketmq consumer, consumer group is empty")
+ }
+ consumerGroup := clientConfig.ConsumerGroup
+ if isBroadCasting {
+ consumerGroup = fmt.Sprintf("%s-%s", constants.ConsumerGroupBroadcastPrefix, consumerGroup)
+ }
+ options = append(options, consumer.WithGroupName(clientConfig.ConsumerGroup))
+
+ // TODO consumeTimeout config, currently rocket mq go client doesn't support
+
+ // instance name
+ if len(clientConfig.InstanceName) != 0 {
+ options = append(options, consumer.WithInstance(clientConfig.InstanceName))
+ }
+
+ return options, nil
+}
diff --git a/plugin/connector/rocketmq/client/rocketmq_producer.go b/plugin/connector/rocketmq/client/rocketmq_producer.go
new file mode 100644
index 0000000..934bcdb
--- /dev/null
+++ b/plugin/connector/rocketmq/client/rocketmq_producer.go
@@ -0,0 +1,168 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package client
+
+import (
+ "context"
+ "encoding/json"
+ "errors"
+ "github.com/apache/incubator-eventmesh/eventmesh-server-go/plugin/connector/rocketmq/config"
+ "github.com/apache/incubator-eventmesh/eventmesh-server-go/plugin/connector/rocketmq/utils"
+ "github.com/apache/rocketmq-client-go/v2"
+ "github.com/apache/rocketmq-client-go/v2/primitive"
+ "github.com/apache/rocketmq-client-go/v2/producer"
+ "strconv"
+ "strings"
+ "time"
+)
+
+// RocketMQProducer RocketMQ producer interface
+type RocketMQProducer interface {
+ Start() error
+ Shutdown() error
+ SendSync(ctx context.Context, msg ...*primitive.Message) (*primitive.SendResult, error)
+ SendAsync(ctx context.Context, mq func(ctx context.Context, result *primitive.SendResult, err error),
+ msg ...*primitive.Message) error
+ SendOneWay(ctx context.Context, msg ...*primitive.Message) error
+ Request(ctx context.Context, ttl time.Duration, msg *primitive.Message) (*primitive.Message, error)
+ RequestAsync(ctx context.Context, ttl time.Duration, callback func(ctx context.Context,
+ msg *primitive.Message, err error), msg *primitive.Message) error
+}
+
+// RocketMQProducerWrapper producer wrapper of RocketMQ client
+type RocketMQProducerWrapper struct {
+ options []producer.Option
+ producer rocketmq.Producer
+}
+
+// NewRocketMQProducerWrapper get a producer wrapper of RocketMQ client
+func NewRocketMQProducerWrapper(properties map[string]string) (*RocketMQProducerWrapper, error) {
+ options, err := getOptionsProducerFromProperties(properties)
+ if err != nil {
+ return nil, err
+ }
+
+ rocketmqProducer, err := rocketmq.NewProducer(options...)
+ if err != nil {
+ return nil, err
+ }
+
+ return &RocketMQProducerWrapper{
+ options: options,
+ producer: rocketmqProducer,
+ }, nil
+}
+
+// Start wrapper start function
+func (r *RocketMQProducerWrapper) Start() error {
+ return r.producer.Start()
+}
+
+// Shutdown wrapper shutdown function
+func (r *RocketMQProducerWrapper) Shutdown() error {
+ return r.producer.Shutdown()
+}
+
+// SendSync wrapper send sync function
+func (r *RocketMQProducerWrapper) SendSync(ctx context.Context, msg ...*primitive.Message) (*primitive.SendResult, error) {
+ return r.producer.SendSync(ctx, msg...)
+}
+
+// SendAsync wrapper send async function
+func (r *RocketMQProducerWrapper) SendAsync(ctx context.Context, mq func(ctx context.Context,
+ result *primitive.SendResult, err error), msg ...*primitive.Message) error {
+ return r.producer.SendAsync(ctx, mq, msg...)
+}
+
+// SendOneWay wrapper send one way function
+func (r *RocketMQProducerWrapper) SendOneWay(ctx context.Context, msg ...*primitive.Message) error {
+ return r.producer.SendOneWay(ctx, msg...)
+}
+
+// Request wrapper request function
+func (r *RocketMQProducerWrapper) Request(ctx context.Context, ttl time.Duration,
+ msg *primitive.Message) (*primitive.Message, error) {
+ return r.producer.Request(ctx, ttl, msg)
+}
+
+// RequestAsync wrapper request async function
+func (r *RocketMQProducerWrapper) RequestAsync(ctx context.Context, ttl time.Duration,
+ callback func(ctx context.Context, msg *primitive.Message, err error), msg *primitive.Message) error {
+ return r.producer.RequestAsync(ctx, ttl, callback, msg)
+}
+
+// getOptionsProducerFromProperties convert properties map to client options
+func getOptionsProducerFromProperties(properties map[string]string) ([]producer.Option, error) {
+ clientConfig, err := getClientConfigFromProperties(properties)
+ if clientConfig == nil {
+ return nil, err
+ }
+ options := make([]producer.Option, 0)
+
+ accessPoints := clientConfig.AccessPoints
+ if len(accessPoints) == 0 {
+ return nil, errors.New("fail to parse rocketmq producer config, invalid access points")
+ }
+
+ // name server address
+ options = append(options, producer.WithNameServer(strings.Split(accessPoints, ",")))
+
+ // instance name
+ producerId := utils.GetInstanceName()
+ options = append(options, producer.WithInstanceName(producerId))
+ clientConfig.InstanceName = producerId
+
+ // producer group name
+ options = append(options, producer.WithGroupName(clientConfig.ProducerGroupName))
+
+ // send msg timeout
+ if len(clientConfig.SendMsgTimeout) != 0 {
+ sendMsgTimeout, err := strconv.Atoi(clientConfig.SendMsgTimeout)
+ if err == nil {
+ options = append(options, producer.WithSendMsgTimeout(time.Duration(sendMsgTimeout)*time.Millisecond))
+ }
+ }
+
+ // producer retry time
+ if len(clientConfig.ProducerRetryTimes) != 0 {
+ producerRetryTimes, err := strconv.Atoi(clientConfig.ProducerRetryTimes)
+ if err == nil {
+ options = append(options, producer.WithRetry(producerRetryTimes))
+ }
+ }
+
+ // producer compress message body threshold
+ if len(clientConfig.CompressMsgBodyThreshold) != 0 {
+ compressMsgBodyThreshold, err := strconv.Atoi(clientConfig.CompressMsgBodyThreshold)
+ if err == nil {
+ options = append(options, producer.WithCompressMsgBodyOverHowmuch(compressMsgBodyThreshold))
+ }
+ }
+ return options, nil
+}
+
+func getClientConfigFromProperties(properties map[string]string) (*config.ClientConfig, error) {
+ arr, err := json.Marshal(properties)
+ if err != nil {
+ return nil, err
+ }
+ clientConfig := &config.ClientConfig{}
+ err = json.Unmarshal(arr, clientConfig)
+ if err != nil {
+ return nil, err
+ }
+ return clientConfig, nil
+}
diff --git a/plugin/connector/rocketmq/config/client_config.go b/plugin/connector/rocketmq/config/client_config.go
new file mode 100644
index 0000000..12b291a
--- /dev/null
+++ b/plugin/connector/rocketmq/config/client_config.go
@@ -0,0 +1,41 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package config
+
+// ClientConfig config of RocketMQ client
+type ClientConfig struct {
+ // AccessPoints addresses of rocketmq name servers, join by ";"
+ AccessPoints string `json:"access_points"`
+ // Namespace client namespace
+ Namespace string `json:"namespace"`
+ // InstanceName client instance name
+ InstanceName string `json:"instance_name"`
+ // ProducerGroupName producer client group name
+ ProducerGroupName string `json:"group_name"`
+ // SendMsgTimeout producer send message timeout (ms), default 3000ms
+ SendMsgTimeout string `json:"send_msg_timeout"`
+ // ProducerRetryTimes producer retry time
+ ProducerRetryTimes string `json:"producer_retry_times"`
+ // CompressMsgBodyThreshold producer compress message body threshold
+ CompressMsgBodyThreshold string `json:"compress_msg_body_threshold"`
+
+ // ConsumerGroup consumer group
+ ConsumerGroup string `json:"consumer_group"`
+ // MaxReconsumeTimes max reconsume times
+ MaxReconsumeTimes string `json:"max_reconsume_times"`
+ // MessageModel message model of consumer, can be BROADCASTING or CLUSTERING
+ MessageModel string `json:"message_model"`
+}
diff --git a/plugin/connector/rocketmq/constants/message_constants.go b/plugin/connector/rocketmq/constants/message_constants.go
new file mode 100644
index 0000000..371cbec
--- /dev/null
+++ b/plugin/connector/rocketmq/constants/message_constants.go
@@ -0,0 +1,25 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package constants
+
+const (
+ CloudEventMessageType = "eventmeshmessage"
+ RocketMQMessageContentTypeProperties = "datacontenttype"
+ MessagePropertySeparator = "99"
+ ConsumerGroupBroadcastPrefix = "broadcast"
+ PropertyMessageBornTimestamp = "borntimestamp"
+ PropertyMessageStoreTimestamp = "storetimestamp"
+)
diff --git a/plugin/connector/rocketmq/constants/rocketmq_message_properties.go b/plugin/connector/rocketmq/constants/rocketmq_message_properties.go
new file mode 100644
index 0000000..3b0b8b0
--- /dev/null
+++ b/plugin/connector/rocketmq/constants/rocketmq_message_properties.go
@@ -0,0 +1,61 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package constants
+
+import (
+ gs "github.com/deckarep/golang-set/v2"
+)
+
+// RocketMQMessageProperties Properties of RocketMQ Message
+var RocketMQMessageProperties gs.Set[string]
+
+func init() {
+ RocketMQMessageProperties = gs.NewSet[string]()
+ RocketMQMessageProperties.Add(" ")
+ RocketMQMessageProperties.Add("KEYS")
+ RocketMQMessageProperties.Add("TAGS")
+ RocketMQMessageProperties.Add("WAIT")
+ RocketMQMessageProperties.Add("DELAY")
+ RocketMQMessageProperties.Add("RETRY_TOPIC")
+ RocketMQMessageProperties.Add("REAL_TOPIC")
+ RocketMQMessageProperties.Add("REAL_QID")
+ RocketMQMessageProperties.Add("TRAN_MSG")
+ RocketMQMessageProperties.Add("PGROUP")
+ RocketMQMessageProperties.Add("MIN_OFFSET")
+ RocketMQMessageProperties.Add("MAX_OFFSET")
+ RocketMQMessageProperties.Add("BUYER_ID")
+ RocketMQMessageProperties.Add("ORIGIN_MESSAGE_ID")
+ RocketMQMessageProperties.Add("TRANSFER_FLAG")
+ RocketMQMessageProperties.Add("CORRECTION_FLAG")
+ RocketMQMessageProperties.Add("MQ2_FLAG")
+ RocketMQMessageProperties.Add("RECONSUME_TIME")
+ RocketMQMessageProperties.Add("MSG_REGION")
+ RocketMQMessageProperties.Add("TRACE_ON")
+ RocketMQMessageProperties.Add("UNIQ_KEY")
+ RocketMQMessageProperties.Add("MAX_RECONSUME_TIMES")
+ RocketMQMessageProperties.Add("CONSUME_START_TIME")
+ RocketMQMessageProperties.Add("TRAN_PREPARED_QUEUE_OFFSET")
+ RocketMQMessageProperties.Add("TRANSACTION_CHECK_TIMES")
+ RocketMQMessageProperties.Add("CHECK_IMMUNITY_TIME_IN_SECONDS")
+ RocketMQMessageProperties.Add("SHARDING_KEY")
+ RocketMQMessageProperties.Add("__transactionId__")
+ RocketMQMessageProperties.Add("CORRELATION_ID")
+ RocketMQMessageProperties.Add("REPLY_TO_CLIENT")
+ RocketMQMessageProperties.Add("TTL")
+ RocketMQMessageProperties.Add("ARRIVE_TIME")
+ RocketMQMessageProperties.Add("MSG_TYPE")
+ RocketMQMessageProperties.Add("CLUSTER")
+}
diff --git a/plugin/connector/rocketmq/consumer.go b/plugin/connector/rocketmq/consumer.go
new file mode 100644
index 0000000..d7fcc77
--- /dev/null
+++ b/plugin/connector/rocketmq/consumer.go
@@ -0,0 +1,221 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package rocketmq
+
+import (
+ "context"
+ "errors"
+ "github.com/apache/incubator-eventmesh/eventmesh-server-go/plugin/connector"
+ "github.com/apache/incubator-eventmesh/eventmesh-server-go/plugin/connector/rocketmq/client"
+ "github.com/apache/incubator-eventmesh/eventmesh-server-go/plugin/connector/rocketmq/constants"
+ "github.com/apache/incubator-eventmesh/eventmesh-server-go/plugin/connector/rocketmq/convert"
+ "github.com/apache/rocketmq-client-go/v2/consumer"
+ "github.com/apache/rocketmq-client-go/v2/primitive"
+ ce "github.com/cloudevents/sdk-go/v2"
+ "strconv"
+ "sync"
+)
+
+type Consumer struct {
+ rocketMQConsumer client.RocketMQConsumer
+ started bool
+ mutex sync.Mutex
+ listener *connector.EventListener
+ subscribeHandler SubscribeHandler
+}
+
+// NewConsumer get new consumer, needs to be Initiated before using
+func NewConsumer() *Consumer {
+ return &Consumer{}
+}
+
+// InitConsumer init consumer by properties
+func (c *Consumer) InitConsumer(properties map[string]string) error {
+ consumer, err := client.NewRocketMQConsumerWrapper(properties)
+ if err != nil {
+ return err
+ }
+ c.rocketMQConsumer = consumer
+ if c.rocketMQConsumer.IsBroadCasting() {
+ c.subscribeHandler = &BroadCastingMessageSubscribeHandler{consumer: c}
+ } else {
+ c.subscribeHandler = &ClusteringMessageSubscribeHandler{consumer: c}
+ }
+ return nil
+}
+
+// IsStarted check if consumer is started
+func (c *Consumer) IsStarted() bool {
+ return c.rocketMQConsumer != nil && c.started
+}
+
+// IsClosed check if consumer is closed
+func (c *Consumer) IsClosed() bool {
+ return c.rocketMQConsumer != nil && !c.started
+}
+
+// Start make consumer started
+func (c *Consumer) Start() error {
+ if c.rocketMQConsumer == nil {
+ return errors.New("start rocketmq consumer fail, producer should be initiated first")
+ }
+ c.mutex.Lock()
+ defer c.mutex.Unlock()
+ if !c.started {
+ err := c.rocketMQConsumer.Start()
+ if err != nil {
+ return err
+ }
+ c.started = true
+ }
+ return nil
+}
+
+// Shutdown terminate the consumer
+func (c *Consumer) Shutdown() error {
+ if c.rocketMQConsumer == nil {
+ return errors.New("shutdown rocketmq consumer fail, producer should be initiated first")
+ }
+ c.mutex.Lock()
+ defer c.mutex.Unlock()
+ if c.started {
+ err := c.rocketMQConsumer.Shutdown()
+ if err != nil {
+ return err
+ }
+ c.started = false
+ }
+ return nil
+}
+
+// UpdateOffset always return error, since currently RocketMQ client doesn't support manual offset updating
+func (c *Consumer) UpdateOffset(ctx context.Context, events []*ce.Event) error {
+ // TODO support offset update
+ return errors.New("fail to update offset, currently RocketMQ client doesn't support manual offset updating")
+}
+
+// Subscribe subscribe topic
+func (c *Consumer) Subscribe(topicName string) error {
+ return c.rocketMQConsumer.Subscribe(topicName, consumer.MessageSelector{}, c.subscribeHandler.handle)
+}
+
+// Unsubscribe unsubscribe topic
+func (c *Consumer) Unsubscribe(topicName string) error {
+ return c.rocketMQConsumer.Unsubscribe(topicName)
+}
+
+// RegisterEventListener listener's Consume function will be called when message is being consumed
+func (c *Consumer) RegisterEventListener(listener *connector.EventListener) {
+ c.listener = listener
+}
+
+// SubscribeHandler interface of message consume handler
+type SubscribeHandler interface {
+ handle(ctx context.Context, msg ...*primitive.MessageExt) (consumer.ConsumeResult, error)
+ getMessageModel() consumer.MessageModel
+}
+
+// ClusteringMessageSubscribeHandler message consume handler of Clustering mode
+type ClusteringMessageSubscribeHandler struct {
+ consumer *Consumer
+}
+
+func (h *ClusteringMessageSubscribeHandler) getMessageModel() consumer.MessageModel {
+ return consumer.Clustering
+}
+
+func (h *ClusteringMessageSubscribeHandler) handle(ctx context.Context, msg ...*primitive.MessageExt) (consumer.ConsumeResult, error) {
+ if len(msg) == 0 {
+ return consumer.ConsumeSuccess, nil
+ }
+
+ messageExt := msg[0]
+ messageExt.WithProperty(constants.PropertyMessageBornTimestamp,
+ strconv.FormatInt(messageExt.BornTimestamp, 10))
+ messageExt.WithProperty(constants.PropertyMessageStoreTimestamp,
+ strconv.FormatInt(messageExt.StoreTimestamp, 10))
+
+ message := &messageExt.Message
+ convert.TransferMessageSystemProperties(message)
+
+ event, err := convert.NewRocketMQMessageReader(message).ToCloudEvent(context.Background())
+ if err != nil {
+ return consumer.ConsumeSuccess, nil
+ }
+
+ consumeResult := consumer.ConsumeSuccess
+ commitFunction := func(action connector.EventMeshAction) error {
+ switch action {
+ case connector.CommitMessage:
+ consumeResult = consumer.ConsumeSuccess
+ case connector.ReconsumeLater:
+ consumeResult = consumer.ConsumeRetryLater
+ case connector.ManualAck:
+ // currently, RocketMQ go client doesn't support manual offset updating, so just commit message here
+ // TODO support manual offset updating
+ consumeResult = consumer.ConsumeSuccess
+ }
+ return nil
+ }
+ h.consumer.listener.Consume(event, commitFunction)
+ return consumeResult, nil
+}
+
+// BroadCastingMessageSubscribeHandler message consume handler of BroadCasting mode
+type BroadCastingMessageSubscribeHandler struct {
+ consumer *Consumer
+}
+
+func (h *BroadCastingMessageSubscribeHandler) getMessageModel() consumer.MessageModel {
+ return consumer.BroadCasting
+}
+
+func (h *BroadCastingMessageSubscribeHandler) handle(ctx context.Context, msg ...*primitive.MessageExt) (consumer.ConsumeResult, error) {
+ if len(msg) == 0 {
+ return consumer.ConsumeSuccess, nil
+ }
+
+ messageExt := msg[0]
+ messageExt.WithProperty(constants.PropertyMessageBornTimestamp,
+ strconv.FormatInt(messageExt.BornTimestamp, 10))
+ messageExt.WithProperty(constants.PropertyMessageStoreTimestamp,
+ strconv.FormatInt(messageExt.StoreTimestamp, 10))
+
+ message := &messageExt.Message
+ convert.TransferMessageSystemProperties(message)
+
+ event, err := convert.NewRocketMQMessageReader(message).ToCloudEvent(context.Background())
+ if err != nil {
+ return consumer.ConsumeSuccess, nil
+ }
+
+ consumeResult := consumer.ConsumeSuccess
+ commitFunction := func(action connector.EventMeshAction) error {
+ switch action {
+ case connector.CommitMessage:
+ consumeResult = consumer.ConsumeSuccess
+ case connector.ReconsumeLater:
+ consumeResult = consumer.ConsumeRetryLater
+ case connector.ManualAck:
+ // currently, RocketMQ go client doesn't support manual offset updating, so just commit message here
+ // TODO support manual offset updating
+ consumeResult = consumer.ConsumeSuccess
+ }
+ return nil
+ }
+ h.consumer.listener.Consume(event, commitFunction)
+ return consumeResult, nil
+}
diff --git a/plugin/connector/rocketmq/consumer_test.go b/plugin/connector/rocketmq/consumer_test.go
new file mode 100644
index 0000000..a801cca
--- /dev/null
+++ b/plugin/connector/rocketmq/consumer_test.go
@@ -0,0 +1,130 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package rocketmq
+
+import (
+ "context"
+ "github.com/apache/incubator-eventmesh/eventmesh-server-go/plugin/connector"
+ "github.com/apache/incubator-eventmesh/eventmesh-server-go/plugin/connector/rocketmq/client"
+ "github.com/apache/incubator-eventmesh/eventmesh-server-go/plugin/connector/rocketmq/convert"
+ "github.com/apache/incubator-eventmesh/eventmesh-server-go/plugin/connector/rocketmq/mock"
+ rmq "github.com/apache/rocketmq-client-go/v2/consumer"
+ "github.com/apache/rocketmq-client-go/v2/primitive"
+ ce "github.com/cloudevents/sdk-go/v2"
+ "github.com/golang/mock/gomock"
+ "github.com/stretchr/testify/require"
+ "testing"
+)
+
+func GetConsumerWithMockClient(ctl *gomock.Controller) *Consumer {
+ consumer := NewConsumer()
+ consumer.InitConsumer(make(map[string]string))
+ mockRocketMQConsumer := mock.NewMockRocketMQConsumer(ctl)
+ mockRocketMQConsumer.EXPECT().Start().Return(nil)
+ mockRocketMQConsumer.EXPECT().Shutdown().Return(nil)
+ consumer.rocketMQConsumer = mockRocketMQConsumer
+ consumer.subscribeHandler = &ClusteringMessageSubscribeHandler{consumer: consumer}
+ return consumer
+}
+
+func TestConsumer_Start(t *testing.T) {
+ ctrl := gomock.NewController(t)
+ defer ctrl.Finish()
+ consumer := GetConsumerWithMockClient(ctrl)
+ consumer.Start()
+ require.True(t, consumer.IsStarted())
+ consumer.Shutdown()
+}
+
+func TestConsumer_Shutdown(t *testing.T) {
+ ctrl := gomock.NewController(t)
+ defer ctrl.Finish()
+ consumer := GetConsumerWithMockClient(ctrl)
+ consumer.Start()
+ consumer.Shutdown()
+ require.False(t, consumer.IsStarted())
+}
+
+func TestConsumer_Subscribe(t *testing.T) {
+ ctrl := gomock.NewController(t)
+ defer ctrl.Finish()
+ consumer := GetConsumerWithMockClient(ctrl)
+ consumer.Start()
+
+ listener := &connector.EventListener{
+ Consume: func(event *ce.Event, commitFunc connector.CommitFunc) error {
+ require.True(t, event.Subject() == "TopicTest")
+ commitFunc(connector.CommitMessage)
+ return nil
+ },
+ }
+
+ consumer.RegisterEventListener(listener)
+
+ consumer.rocketMQConsumer.(*mock.MockRocketMQConsumer).EXPECT().
+ Subscribe(gomock.Any(), gomock.Any(), gomock.Any()).
+ Do(
+ func(topic string, selector rmq.MessageSelector, f client.SubscribeFunc) {
+ messageExt := &primitive.MessageExt{
+ Message: *GetFullMessage(),
+ MsgId: "MockMsgId",
+ OffsetMsgId: "MockOffsetMsgId",
+ }
+ result, err := f(context.Background(), messageExt)
+ require.False(t, err != nil)
+ require.True(t, result == rmq.ConsumeSuccess)
+ },
+ )
+
+ consumer.Subscribe("TopicTest")
+ consumer.Shutdown()
+}
+
+//func TestConsumer_Subscribe_Broker(t *testing.T) {
+// properties := make(map[string]string)
+// properties["access_points"] = "127.0.0.1:9876"
+// properties["consumer_group"] = "test_group"
+// consumer := NewConsumer()
+// consumer.InitConsumer(properties)
+// consumer.Start()
+//
+// listener := &connector.EventListener{
+// Consume: func(event *ce.Event, commitFunc connector.CommitFunc) error {
+// fmt.Println(event)
+// commitFunc(connector.CommitMessage)
+// return nil
+// },
+// }
+//
+// consumer.RegisterEventListener(listener)
+// err := consumer.Subscribe("TopicTest")
+// if err != nil {
+// panic(err)
+// }
+//
+// time.Sleep(100 * time.Second)
+//}
+
+func GetFullMessage() *primitive.Message {
+ event := ce.NewEvent()
+ event.SetSubject("TopicTest")
+ event.SetID("MockEventId")
+ event.SetDataContentType("application/json")
+ event.SetExtension("protocoltype", "http")
+ event.SetData("application/json", "{\"data\":\"foo\"}")
+ message, _ := convert.NewRocketMQMessageWriter("test-topic").ToMessage(context.Background(), &event)
+ return message
+}
diff --git a/plugin/connector/rocketmq/convert/message_ext_convert.go b/plugin/connector/rocketmq/convert/message_ext_convert.go
new file mode 100644
index 0000000..0b3d9c0
--- /dev/null
+++ b/plugin/connector/rocketmq/convert/message_ext_convert.go
@@ -0,0 +1,33 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package convert
+
+import (
+ "github.com/apache/incubator-eventmesh/eventmesh-server-go/plugin/connector/rocketmq/constants"
+ "github.com/apache/rocketmq-client-go/v2/primitive"
+ "strings"
+)
+
+// TransferMessageSystemProperties re-set message system properties to fix pattern
+func TransferMessageSystemProperties(message *primitive.Message) {
+ for propertyKey := range constants.RocketMQMessageProperties.Iter() {
+ if val := message.GetProperty(propertyKey); len(val) != 0 {
+ key := strings.ReplaceAll(strings.ToLower(propertyKey), "_", constants.MessagePropertySeparator)
+ message.WithProperty(key, val)
+ message.RemoveProperty(propertyKey)
+ }
+ }
+}
diff --git a/plugin/connector/rocketmq/convert/rocketmq_message_reader.go b/plugin/connector/rocketmq/convert/rocketmq_message_reader.go
new file mode 100644
index 0000000..8d22baa
--- /dev/null
+++ b/plugin/connector/rocketmq/convert/rocketmq_message_reader.go
@@ -0,0 +1,110 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package convert
+
+import (
+ "bytes"
+ "context"
+ "github.com/apache/incubator-eventmesh/eventmesh-server-go/plugin/connector/rocketmq/constants"
+ "github.com/apache/rocketmq-client-go/v2/primitive"
+ ce "github.com/cloudevents/sdk-go/v2"
+ "github.com/cloudevents/sdk-go/v2/binding"
+ "github.com/cloudevents/sdk-go/v2/binding/spec"
+ "github.com/cloudevents/sdk-go/v2/event"
+)
+
+// RocketMQMessageReader cloud event message reader
+type RocketMQMessageReader struct {
+ topic string
+ properties map[string]string
+ body []byte
+ version spec.Version
+}
+
+// NewRocketMQMessageReader get RocketMQ message reader
+func NewRocketMQMessageReader(message *primitive.Message) *RocketMQMessageReader {
+ return &RocketMQMessageReader{
+ topic: message.Topic,
+ properties: message.GetProperties(),
+ body: message.Body,
+ version: spec.VS.Version(event.CloudEventsVersionV1),
+ }
+}
+
+// ReadEncoding the RocketMQMessageReader only supports binary encoding
+func (r *RocketMQMessageReader) ReadEncoding() binding.Encoding {
+ return binding.EncodingBinary
+}
+
+// ReadBinary read message and write through encoder
+func (r *RocketMQMessageReader) ReadBinary(ctx context.Context, encoder binding.BinaryWriter) (err error) {
+ subject := r.version.Attribute("subject")
+ encoder.SetAttribute(subject, r.topic)
+
+ msgType := r.version.Attribute("type")
+ encoder.SetAttribute(msgType, constants.CloudEventMessageType)
+
+ contentType := r.version.Attribute("datacontenttype")
+ encoder.SetAttribute(contentType, r.properties[constants.RocketMQMessageContentTypeProperties])
+
+ for k, v := range r.properties {
+ attr := r.version.Attribute(k)
+ if attr != nil {
+ err = encoder.SetAttribute(attr, v)
+ } else {
+ err = encoder.SetExtension(k, v)
+ }
+ if err != nil {
+ return err
+ }
+ }
+
+ if r.body != nil {
+ err = encoder.SetData(bytes.NewReader(r.body))
+ if err != nil {
+ return err
+ }
+ }
+ return
+}
+
+// GetAttribute get attribute
+func (r *RocketMQMessageReader) GetAttribute(k spec.Kind) (spec.Attribute, interface{}) {
+ attr := r.version.Attribute(k.String())
+ if attr != nil {
+ return attr, r.properties[k.String()]
+ }
+ return attr, nil
+}
+
+// GetExtension get extension
+func (r *RocketMQMessageReader) GetExtension(s string) interface{} {
+ return r.properties[s]
+}
+
+// ReadStructured the RocketMQMessageReader only supports binary encoding
+func (r *RocketMQMessageReader) ReadStructured(context.Context, binding.StructuredWriter) error {
+ return binding.ErrNotStructured
+}
+
+// ToCloudEvent convert RocketMQ message to cloud event
+func (r *RocketMQMessageReader) ToCloudEvent(ctx context.Context) (*ce.Event, error) {
+ event, err := binding.ToEvent(ctx, r)
+ if err != nil {
+ return nil, err
+ }
+ return event, nil
+}
diff --git a/plugin/connector/rocketmq/convert/rocketmq_message_reader_test.go b/plugin/connector/rocketmq/convert/rocketmq_message_reader_test.go
new file mode 100644
index 0000000..3ac3946
--- /dev/null
+++ b/plugin/connector/rocketmq/convert/rocketmq_message_reader_test.go
@@ -0,0 +1,47 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package convert
+
+import (
+ "context"
+ "github.com/apache/incubator-eventmesh/eventmesh-server-go/plugin/connector/rocketmq/constants"
+ "github.com/apache/rocketmq-client-go/v2/primitive"
+ "github.com/cloudevents/sdk-go/v2/event"
+ "github.com/cloudevents/sdk-go/v2/event/datacodec"
+ "github.com/stretchr/testify/require"
+ "testing"
+)
+
+func TestRocketMQMessageReader_ToCloudEvent(t *testing.T) {
+ e, err := NewRocketMQMessageReader(getTestMessage()).ToCloudEvent(context.Background())
+ require.True(t, err == nil)
+ require.True(t, e.ID() == "MockEventId")
+ require.True(t, e.Extensions()["protocoltype"] == "http")
+ data, _ := datacodec.Encode(context.Background(), event.ApplicationJSON, "{\"data\":\"foo\"}")
+ require.True(t, string(e.Data()) == string(data))
+}
+
+func getTestMessage() *primitive.Message {
+ data, _ := datacodec.Encode(context.Background(), event.ApplicationJSON, "{\"data\":\"foo\"}")
+ message := &primitive.Message{
+ Topic: "TopicTest",
+ Body: data,
+ }
+ message.WithProperty("id", "MockEventId")
+ message.WithProperty(constants.RocketMQMessageContentTypeProperties, event.ApplicationJSON)
+ message.WithProperty("protocoltype", "http")
+ return message
+}
diff --git a/plugin/connector/rocketmq/convert/rocketmq_message_writer.go b/plugin/connector/rocketmq/convert/rocketmq_message_writer.go
new file mode 100644
index 0000000..d6185ff
--- /dev/null
+++ b/plugin/connector/rocketmq/convert/rocketmq_message_writer.go
@@ -0,0 +1,86 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package convert
+
+import (
+ "context"
+ "github.com/apache/rocketmq-client-go/v2/primitive"
+ ce "github.com/cloudevents/sdk-go/v2"
+ "github.com/cloudevents/sdk-go/v2/binding"
+ "github.com/cloudevents/sdk-go/v2/binding/spec"
+ "github.com/cloudevents/sdk-go/v2/types"
+ "io"
+ "io/ioutil"
+)
+
+// RocketMQMessageWriter cloud event message writer
+type RocketMQMessageWriter struct {
+ message *primitive.Message
+}
+
+// NewRocketMQMessageWriter get RocketMQ message writer
+func NewRocketMQMessageWriter(topic string) *RocketMQMessageWriter {
+ message := &primitive.Message{}
+ message.Topic = topic
+ return &RocketMQMessageWriter{message: message}
+}
+
+// SetAttribute set attribute
+func (r *RocketMQMessageWriter) SetAttribute(attribute spec.Attribute, value interface{}) error {
+ val, err := types.Format(value)
+ if err != nil {
+ return err
+ }
+ r.message.WithProperty(attribute.Name(), val)
+ return nil
+}
+
+// SetExtension set extension
+func (r *RocketMQMessageWriter) SetExtension(name string, value interface{}) error {
+ r.message.WithProperty(name, value.(string))
+ return nil
+}
+
+// Start to start writing, do nothing
+func (r *RocketMQMessageWriter) Start(ctx context.Context) error {
+ // No-Op
+ return nil
+}
+
+// SetData set data from reader
+func (r *RocketMQMessageWriter) SetData(data io.Reader) error {
+ b, err := ioutil.ReadAll(data)
+ if err != nil {
+ return nil
+ }
+ r.message.Body = b
+ return nil
+}
+
+// End the end of writing, do nothing
+func (r *RocketMQMessageWriter) End(ctx context.Context) error {
+ // No-Op
+ return nil
+}
+
+// ToMessage convert cloud event to RocketMQ message
+func (r *RocketMQMessageWriter) ToMessage(ctx context.Context, cloudEvent *ce.Event) (*primitive.Message, error) {
+ _, err := binding.Write(binding.WithForceBinary(ctx), (*binding.EventMessage)(cloudEvent), nil, r)
+ if err != nil {
+ return nil, err
+ }
+ return r.message, err
+}
diff --git a/plugin/connector/rocketmq/convert/rocketmq_message_writer_test.go b/plugin/connector/rocketmq/convert/rocketmq_message_writer_test.go
new file mode 100644
index 0000000..6835b26
--- /dev/null
+++ b/plugin/connector/rocketmq/convert/rocketmq_message_writer_test.go
@@ -0,0 +1,44 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package convert
+
+import (
+ "context"
+ ce "github.com/cloudevents/sdk-go/v2"
+ "github.com/cloudevents/sdk-go/v2/event"
+ "github.com/cloudevents/sdk-go/v2/event/datacodec"
+ "github.com/stretchr/testify/require"
+ "testing"
+)
+
+func TestWriteBinary(t *testing.T) {
+ message, err := NewRocketMQMessageWriter("test-topic").ToMessage(context.Background(), getTestEvent())
+ require.True(t, err == nil)
+ require.True(t, message.GetProperty("id") == "MockEventId")
+ require.True(t, message.GetProperty("protocoltype") == "http")
+ data, _ := datacodec.Encode(context.Background(), event.ApplicationJSON, "{\"data\":\"foo\"}")
+ require.True(t, string(message.Body) == string(data))
+}
+
+func getTestEvent() *ce.Event {
+ event := ce.NewEvent()
+ event.SetSubject("TopicTest")
+ event.SetID("MockEventId")
+ event.SetDataContentType("application/json")
+ event.SetExtension("protocoltype", "http")
+ event.SetData("application/json", "{\"data\":\"foo\"}")
+ return &event
+}
diff --git a/plugin/connector/rocketmq/factory.go b/plugin/connector/rocketmq/factory.go
new file mode 100644
index 0000000..78de461
--- /dev/null
+++ b/plugin/connector/rocketmq/factory.go
@@ -0,0 +1,84 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package rocketmq
+
+import (
+ "errors"
+ "github.com/apache/incubator-eventmesh/eventmesh-server-go/plugin"
+ "github.com/apache/incubator-eventmesh/eventmesh-server-go/plugin/connector"
+)
+
+func init() {
+ plugin.Register("rocketmq", &Factory{})
+}
+
+type Factory struct {
+ plugin.Plugin
+ properties map[string]string
+ producer connector.Producer
+ consumer connector.Consumer
+}
+
+func (f *Factory) Type() string {
+ return connector.PluginType
+}
+
+func (f *Factory) Setup(name string, dec plugin.Decoder) error {
+ if dec == nil {
+ return errors.New(" producer config decoder empty")
+ }
+ properties := make(map[string]string)
+ if err := dec.Decode(properties); err != nil {
+ return err
+ }
+ f.properties = properties
+
+ consumer := NewConsumer()
+ err := consumer.InitConsumer(f.properties)
+ if err != nil {
+ return err
+ }
+ err = consumer.Start()
+ if err != nil {
+ return err
+ }
+ f.consumer = consumer
+
+ producer := NewProducer()
+ err = producer.InitProducer(f.properties)
+ if err != nil {
+ return err
+ }
+ err = producer.Start()
+ if err != nil {
+ return err
+ }
+ f.producer = producer
+
+ return nil
+}
+
+func (f *Factory) GetProducer() (connector.Producer, error) {
+ return f.producer, nil
+}
+
+func (f *Factory) GetConsumer() (connector.Consumer, error) {
+ return f.consumer, nil
+}
+
+func (f *Factory) GetResource() (connector.Resource, error) {
+ return &Resource{}, nil
+}
diff --git a/plugin/connector/rocketmq/mock/mock_rocketmq_consumer.go b/plugin/connector/rocketmq/mock/mock_rocketmq_consumer.go
new file mode 100644
index 0000000..0e464cc
--- /dev/null
+++ b/plugin/connector/rocketmq/mock/mock_rocketmq_consumer.go
@@ -0,0 +1,145 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Code generated by MockGen. DO NOT EDIT.
+// Source: /Users/chenzhou/horoc/incubator-eventmesh/eventmesh-server-go/plugin/connector/rocketmq/client/rocketmq_consumer.go
+
+// Package mock is a generated GoMock package.
+package mock
+
+import (
+ reflect "reflect"
+
+ client "github.com/apache/incubator-eventmesh/eventmesh-server-go/plugin/connector/rocketmq/client"
+ consumer "github.com/apache/rocketmq-client-go/v2/consumer"
+ gomock "github.com/golang/mock/gomock"
+)
+
+// MockRocketMQConsumer is a mock of RocketMQConsumer interface.
+type MockRocketMQConsumer struct {
+ ctrl *gomock.Controller
+ recorder *MockRocketMQConsumerMockRecorder
+}
+
+// MockRocketMQConsumerMockRecorder is the mock recorder for MockRocketMQConsumer.
+type MockRocketMQConsumerMockRecorder struct {
+ mock *MockRocketMQConsumer
+}
+
+// NewMockRocketMQConsumer creates a new mock instance.
+func NewMockRocketMQConsumer(ctrl *gomock.Controller) *MockRocketMQConsumer {
+ mock := &MockRocketMQConsumer{ctrl: ctrl}
+ mock.recorder = &MockRocketMQConsumerMockRecorder{mock}
+ return mock
+}
+
+// EXPECT returns an object that allows the caller to indicate expected use.
+func (m *MockRocketMQConsumer) EXPECT() *MockRocketMQConsumerMockRecorder {
+ return m.recorder
+}
+
+// IsBroadCasting mocks base method.
+func (m *MockRocketMQConsumer) IsBroadCasting() bool {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "IsBroadCasting")
+ ret0, _ := ret[0].(bool)
+ return ret0
+}
+
+// IsBroadCasting indicates an expected call of IsBroadCasting.
+func (mr *MockRocketMQConsumerMockRecorder) IsBroadCasting() *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsBroadCasting", reflect.TypeOf((*MockRocketMQConsumer)(nil).IsBroadCasting))
+}
+
+// Resume mocks base method.
+func (m *MockRocketMQConsumer) Resume() {
+ m.ctrl.T.Helper()
+ m.ctrl.Call(m, "Resume")
+}
+
+// Resume indicates an expected call of Resume.
+func (mr *MockRocketMQConsumerMockRecorder) Resume() *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Resume", reflect.TypeOf((*MockRocketMQConsumer)(nil).Resume))
+}
+
+// Shutdown mocks base method.
+func (m *MockRocketMQConsumer) Shutdown() error {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "Shutdown")
+ ret0, _ := ret[0].(error)
+ return ret0
+}
+
+// Shutdown indicates an expected call of Shutdown.
+func (mr *MockRocketMQConsumerMockRecorder) Shutdown() *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Shutdown", reflect.TypeOf((*MockRocketMQConsumer)(nil).Shutdown))
+}
+
+// Start mocks base method.
+func (m *MockRocketMQConsumer) Start() error {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "Start")
+ ret0, _ := ret[0].(error)
+ return ret0
+}
+
+// Start indicates an expected call of Start.
+func (mr *MockRocketMQConsumerMockRecorder) Start() *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Start", reflect.TypeOf((*MockRocketMQConsumer)(nil).Start))
+}
+
+// Subscribe mocks base method.
+func (m *MockRocketMQConsumer) Subscribe(topic string, selector consumer.MessageSelector, f client.SubscribeFunc) error {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "Subscribe", topic, selector, f)
+ ret0, _ := ret[0].(error)
+ return ret0
+}
+
+// Subscribe indicates an expected call of Subscribe.
+func (mr *MockRocketMQConsumerMockRecorder) Subscribe(topic, selector, f interface{}) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Subscribe", reflect.TypeOf((*MockRocketMQConsumer)(nil).Subscribe), topic, selector, f)
+}
+
+// Suspend mocks base method.
+func (m *MockRocketMQConsumer) Suspend() {
+ m.ctrl.T.Helper()
+ m.ctrl.Call(m, "Suspend")
+}
+
+// Suspend indicates an expected call of Suspend.
+func (mr *MockRocketMQConsumerMockRecorder) Suspend() *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Suspend", reflect.TypeOf((*MockRocketMQConsumer)(nil).Suspend))
+}
+
+// Unsubscribe mocks base method.
+func (m *MockRocketMQConsumer) Unsubscribe(topic string) error {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "Unsubscribe", topic)
+ ret0, _ := ret[0].(error)
+ return ret0
+}
+
+// Unsubscribe indicates an expected call of Unsubscribe.
+func (mr *MockRocketMQConsumerMockRecorder) Unsubscribe(topic interface{}) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Unsubscribe", reflect.TypeOf((*MockRocketMQConsumer)(nil).Unsubscribe), topic)
+}
diff --git a/plugin/connector/rocketmq/mock/mock_rocketmq_producer.go b/plugin/connector/rocketmq/mock/mock_rocketmq_producer.go
new file mode 100644
index 0000000..c8b2805
--- /dev/null
+++ b/plugin/connector/rocketmq/mock/mock_rocketmq_producer.go
@@ -0,0 +1,167 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Code generated by MockGen. DO NOT EDIT.
+// Source: /Users/chenzhou/horoc/incubator-eventmesh/eventmesh-server-go/plugin/connector/rocketmq/producer/rocketmq_producer.go
+
+// Package mock is a generated GoMock package.
+package mock
+
+import (
+ context "context"
+ reflect "reflect"
+ time "time"
+
+ primitive "github.com/apache/rocketmq-client-go/v2/primitive"
+ gomock "github.com/golang/mock/gomock"
+)
+
+// MockRocketMQProducer is a mock of RocketMQProducer interface.
+type MockRocketMQProducer struct {
+ ctrl *gomock.Controller
+ recorder *MockRocketMQProducerMockRecorder
+}
+
+// MockRocketMQProducerMockRecorder is the mock recorder for MockRocketMQProducer.
+type MockRocketMQProducerMockRecorder struct {
+ mock *MockRocketMQProducer
+}
+
+// NewMockRocketMQProducer creates a new mock instance.
+func NewMockRocketMQProducer(ctrl *gomock.Controller) *MockRocketMQProducer {
+ mock := &MockRocketMQProducer{ctrl: ctrl}
+ mock.recorder = &MockRocketMQProducerMockRecorder{mock}
+ return mock
+}
+
+// EXPECT returns an object that allows the caller to indicate expected use.
+func (m *MockRocketMQProducer) EXPECT() *MockRocketMQProducerMockRecorder {
+ return m.recorder
+}
+
+// Request mocks base method.
+func (m *MockRocketMQProducer) Request(ctx context.Context, ttl time.Duration, msg *primitive.Message) (*primitive.Message, error) {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "Request", ctx, ttl, msg)
+ ret0, _ := ret[0].(*primitive.Message)
+ ret1, _ := ret[1].(error)
+ return ret0, ret1
+}
+
+// Request indicates an expected call of Request.
+func (mr *MockRocketMQProducerMockRecorder) Request(ctx, ttl, msg interface{}) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Request", reflect.TypeOf((*MockRocketMQProducer)(nil).Request), ctx, ttl, msg)
+}
+
+// RequestAsync mocks base method.
+func (m *MockRocketMQProducer) RequestAsync(ctx context.Context, ttl time.Duration, callback func(context.Context, *primitive.Message, error), msg *primitive.Message) error {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "RequestAsync", ctx, ttl, callback, msg)
+ ret0, _ := ret[0].(error)
+ return ret0
+}
+
+// RequestAsync indicates an expected call of RequestAsync.
+func (mr *MockRocketMQProducerMockRecorder) RequestAsync(ctx, ttl, callback, msg interface{}) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RequestAsync", reflect.TypeOf((*MockRocketMQProducer)(nil).RequestAsync), ctx, ttl, callback, msg)
+}
+
+// SendAsync mocks base method.
+func (m *MockRocketMQProducer) SendAsync(ctx context.Context, mq func(context.Context, *primitive.SendResult, error), msg ...*primitive.Message) error {
+ m.ctrl.T.Helper()
+ varargs := []interface{}{ctx, mq}
+ for _, a := range msg {
+ varargs = append(varargs, a)
+ }
+ ret := m.ctrl.Call(m, "SendAsync", varargs...)
+ ret0, _ := ret[0].(error)
+ return ret0
+}
+
+// SendAsync indicates an expected call of SendAsync.
+func (mr *MockRocketMQProducerMockRecorder) SendAsync(ctx, mq interface{}, msg ...interface{}) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ varargs := append([]interface{}{ctx, mq}, msg...)
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendAsync", reflect.TypeOf((*MockRocketMQProducer)(nil).SendAsync), varargs...)
+}
+
+// SendOneWay mocks base method.
+func (m *MockRocketMQProducer) SendOneWay(ctx context.Context, mq ...*primitive.Message) error {
+ m.ctrl.T.Helper()
+ varargs := []interface{}{ctx}
+ for _, a := range mq {
+ varargs = append(varargs, a)
+ }
+ ret := m.ctrl.Call(m, "SendOneWay", varargs...)
+ ret0, _ := ret[0].(error)
+ return ret0
+}
+
+// SendOneWay indicates an expected call of SendOneWay.
+func (mr *MockRocketMQProducerMockRecorder) SendOneWay(ctx interface{}, mq ...interface{}) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ varargs := append([]interface{}{ctx}, mq...)
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendOneWay", reflect.TypeOf((*MockRocketMQProducer)(nil).SendOneWay), varargs...)
+}
+
+// SendSync mocks base method.
+func (m *MockRocketMQProducer) SendSync(ctx context.Context, mq ...*primitive.Message) (*primitive.SendResult, error) {
+ m.ctrl.T.Helper()
+ varargs := []interface{}{ctx}
+ for _, a := range mq {
+ varargs = append(varargs, a)
+ }
+ ret := m.ctrl.Call(m, "SendSync", varargs...)
+ ret0, _ := ret[0].(*primitive.SendResult)
+ ret1, _ := ret[1].(error)
+ return ret0, ret1
+}
+
+// SendSync indicates an expected call of SendSync.
+func (mr *MockRocketMQProducerMockRecorder) SendSync(ctx interface{}, mq ...interface{}) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ varargs := append([]interface{}{ctx}, mq...)
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendSync", reflect.TypeOf((*MockRocketMQProducer)(nil).SendSync), varargs...)
+}
+
+// Shutdown mocks base method.
+func (m *MockRocketMQProducer) Shutdown() error {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "Shutdown")
+ ret0, _ := ret[0].(error)
+ return ret0
+}
+
+// Shutdown indicates an expected call of Shutdown.
+func (mr *MockRocketMQProducerMockRecorder) Shutdown() *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Shutdown", reflect.TypeOf((*MockRocketMQProducer)(nil).Shutdown))
+}
+
+// Start mocks base method.
+func (m *MockRocketMQProducer) Start() error {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "Start")
+ ret0, _ := ret[0].(error)
+ return ret0
+}
+
+// Start indicates an expected call of Start.
+func (mr *MockRocketMQProducerMockRecorder) Start() *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Start", reflect.TypeOf((*MockRocketMQProducer)(nil).Start))
+}
diff --git a/plugin/connector/rocketmq/producer.go b/plugin/connector/rocketmq/producer.go
new file mode 100644
index 0000000..49f7ca7
--- /dev/null
+++ b/plugin/connector/rocketmq/producer.go
@@ -0,0 +1,236 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package rocketmq
+
+import (
+ "context"
+ "errors"
+ "github.com/apache/incubator-eventmesh/eventmesh-server-go/plugin/connector"
+ "github.com/apache/incubator-eventmesh/eventmesh-server-go/plugin/connector/rocketmq/client"
+ "github.com/apache/incubator-eventmesh/eventmesh-server-go/plugin/connector/rocketmq/constants"
+ "github.com/apache/incubator-eventmesh/eventmesh-server-go/plugin/connector/rocketmq/convert"
+ "github.com/apache/rocketmq-client-go/v2/primitive"
+ ce "github.com/cloudevents/sdk-go/v2"
+ "strings"
+ "sync"
+ "time"
+)
+
+type SendCallback func(ctx context.Context, result *primitive.SendResult, err error)
+
+type Producer struct {
+ rocketMQProducer client.RocketMQProducer
+ started bool
+ mutex sync.Mutex
+}
+
+// NewProducer get new producer, needs to be Initiated before using
+func NewProducer() *Producer {
+ return &Producer{}
+}
+
+// InitProducer init producer by properties
+func (p *Producer) InitProducer(properties map[string]string) error {
+ producer, err := client.NewRocketMQProducerWrapper(properties)
+ if err != nil {
+ return err
+ }
+ p.rocketMQProducer = producer
+ return nil
+}
+
+// IsStarted check if producer is started
+func (p *Producer) IsStarted() bool {
+ return p.rocketMQProducer != nil && p.started
+}
+
+// IsClosed check if producer is closed
+func (p *Producer) IsClosed() bool {
+ return p.rocketMQProducer != nil && !p.started
+}
+
+// Start make producer started
+func (p *Producer) Start() error {
+ if p.rocketMQProducer == nil {
+ return errors.New("start rocketmq producer fail, producer should be initiated first")
+ }
+ p.mutex.Lock()
+ defer p.mutex.Unlock()
+ if !p.started {
+ err := p.rocketMQProducer.Start()
+ if err != nil {
+ return err
+ }
+ p.started = true
+ }
+ return nil
+}
+
+// Shutdown terminate the producer
+func (p *Producer) Shutdown() error {
+ if p.rocketMQProducer == nil {
+ return errors.New("shutdown rocketmq producer fail, producer should be initiated first")
+ }
+ p.mutex.Lock()
+ defer p.mutex.Unlock()
+ if p.started {
+ err := p.rocketMQProducer.Shutdown()
+ if err != nil {
+ return err
+ }
+ p.started = false
+ }
+ return nil
+}
+
+// Publish async publish message to broker
+func (p *Producer) Publish(ctx context.Context, event *ce.Event, callback *connector.SendCallback) error {
+ if err := p.checkProducerStatus(); err != nil {
+ return err
+ }
+ msg, err := convert.NewRocketMQMessageWriter(event.Subject()).ToMessage(ctx, event)
+ if err != nil {
+ return err
+ }
+ p.supplySysProp(msg, event)
+ err = p.rocketMQProducer.SendAsync(ctx, p.sendCallbackConvert(callback), msg)
+ if err != nil {
+ return err
+ }
+ return nil
+}
+
+// SendOneway async send message without callback
+func (p *Producer) SendOneway(ctx context.Context, event *ce.Event) error {
+ if err := p.checkProducerStatus(); err != nil {
+ return err
+ }
+ msg, err := convert.NewRocketMQMessageWriter(event.Subject()).ToMessage(ctx, event)
+ if err != nil {
+ return err
+ }
+ p.supplySysProp(msg, event)
+ err = p.rocketMQProducer.SendOneWay(ctx, msg)
+ if err != nil {
+ return err
+ }
+ return nil
+}
+
+// Request async request message
+func (p *Producer) Request(ctx context.Context, event *ce.Event, callback *connector.RequestReplyCallback,
+ timeout time.Duration) error {
+ if err := p.checkProducerStatus(); err != nil {
+ return err
+ }
+ msg, err := convert.NewRocketMQMessageWriter(event.Subject()).ToMessage(ctx, event)
+ if err != nil {
+ return err
+ }
+ p.supplySysProp(msg, event)
+ return p.rocketMQProducer.RequestAsync(ctx, timeout, p.requestReplyCallbackConvert(event.Subject(), callback), msg)
+}
+
+// Reply async send message to reply
+func (p *Producer) Reply(ctx context.Context, event *ce.Event, callback *connector.SendCallback) error {
+ if err := p.checkProducerStatus(); err != nil {
+ return err
+ }
+ msg, err := convert.NewRocketMQMessageWriter(event.Subject()).ToMessage(ctx, event)
+ if err != nil {
+ return err
+ }
+ p.supplySysProp(msg, event)
+ err = p.rocketMQProducer.SendAsync(ctx, p.sendCallbackConvert(callback))
+ if err != nil {
+ return err
+ }
+ return nil
+}
+
+// CheckTopicExist RocketMQ go-sdk doesn't support topic check
+func (p *Producer) CheckTopicExist(topicName string) (bool, error) {
+ // RocketMQ go-sdk doesn't support topic check
+ return false, errors.New("rocketmq producer doesn't support topic check")
+}
+
+// SetExtFields do nothing, RocketMQ go-sdk doesn't support dynamic client option modify
+func (p *Producer) SetExtFields() error {
+ return nil
+}
+
+// checkProducerStatus check is the producer has started
+func (p *Producer) checkProducerStatus() error {
+ if p.rocketMQProducer == nil {
+ return errors.New("rocketmq producer has not been initiated")
+ }
+
+ if !p.started {
+ return errors.New("rocketmq producer has not started")
+ }
+ return nil
+}
+
+// supplySysProp convert producer client's system properties format
+func (p *Producer) supplySysProp(message *primitive.Message, cloudEvent *ce.Event) {
+ for _, propertyKey := range constants.RocketMQMessageProperties.ToSlice() {
+ key := strings.ReplaceAll(strings.ToLower(propertyKey), "_", constants.MessagePropertySeparator)
+ if val, ok := cloudEvent.Extensions()[key]; ok {
+ message.WithProperty(propertyKey, val.(string))
+ message.RemoveProperty(key)
+ }
+ }
+}
+
+// sendCallbackConvert convert connector API callback to RocketMQ callback
+func (p *Producer) sendCallbackConvert(callback *connector.SendCallback) SendCallback {
+ return func(ctx context.Context, result *primitive.SendResult, err error) {
+ if err != nil {
+ callback.OnError(&connector.ErrorResult{
+ Topic: result.MessageQueue.Topic,
+ Err: err,
+ })
+ }
+ callback.OnSuccess(p.convertToSendResult(result))
+ }
+}
+
+// requestReplyCallbackConvert convert connector API callback to RocketMQ callback
+func (p *Producer) requestReplyCallbackConvert(topic string,
+ callback *connector.RequestReplyCallback) func(ctx context.Context, msg *primitive.Message, err error) {
+ return func(ctx context.Context, msg *primitive.Message, err error) {
+ if err != nil {
+ callback.OnError(&connector.ErrorResult{
+ Topic: topic,
+ Err: err,
+ })
+ return
+ }
+ convert.TransferMessageSystemProperties(msg)
+ event, err := convert.NewRocketMQMessageReader(msg).ToCloudEvent(ctx)
+ if err != nil {
+ panic(err)
+ }
+ callback.OnSuccess(event)
+ }
+}
+
+func (p *Producer) convertToSendResult(result *primitive.SendResult) *connector.SendResult {
+ return &connector.SendResult{
+ MessageId: result.MsgID,
+ Topic: result.MessageQueue.Topic,
+ }
+}
diff --git a/plugin/connector/rocketmq/producer_test.go b/plugin/connector/rocketmq/producer_test.go
new file mode 100644
index 0000000..576fe41
--- /dev/null
+++ b/plugin/connector/rocketmq/producer_test.go
@@ -0,0 +1,214 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package rocketmq
+
+import (
+ "context"
+ "github.com/apache/incubator-eventmesh/eventmesh-server-go/plugin/connector"
+ "github.com/apache/incubator-eventmesh/eventmesh-server-go/plugin/connector/rocketmq/mock"
+ "github.com/apache/rocketmq-client-go/v2/primitive"
+ ce "github.com/cloudevents/sdk-go/v2"
+ "github.com/cloudevents/sdk-go/v2/event"
+ "github.com/cloudevents/sdk-go/v2/event/datacodec"
+ "github.com/golang/mock/gomock"
+ "github.com/stretchr/testify/require"
+ "testing"
+ "time"
+)
+
+func GetProducerWithMockClient(ctl *gomock.Controller) *Producer {
+ producer := NewProducer()
+ producer.InitProducer(make(map[string]string))
+ mockRocketMQProducer := mock.NewMockRocketMQProducer(ctl)
+ mockRocketMQProducer.EXPECT().Start().Return(nil)
+ mockRocketMQProducer.EXPECT().Shutdown().Return(nil)
+ producer.rocketMQProducer = mockRocketMQProducer
+ return producer
+}
+
+func TestProducer_Start(t *testing.T) {
+ ctrl := gomock.NewController(t)
+ defer ctrl.Finish()
+ producer := GetProducerWithMockClient(ctrl)
+ producer.Start()
+ require.True(t, producer.IsStarted())
+ producer.Shutdown()
+}
+
+func TestProducer_Shutdown(t *testing.T) {
+ ctrl := gomock.NewController(t)
+ defer ctrl.Finish()
+ producer := GetProducerWithMockClient(ctrl)
+ producer.Start()
+ producer.Shutdown()
+ require.False(t, producer.IsStarted())
+}
+
+func TestProducer_Publish(t *testing.T) {
+ ctrl := gomock.NewController(t)
+ defer ctrl.Finish()
+ producer := GetProducerWithMockClient(ctrl)
+ producer.Start()
+
+ callback := &connector.SendCallback{
+ OnSuccess: func(result *connector.SendResult) {
+ require.True(t, result.Topic == "TopicTest")
+ require.True(t, result.MessageId == "MockMsgID")
+ },
+ OnError: func(result *connector.ErrorResult) {
+ panic(result.Err)
+ },
+ }
+
+ producer.rocketMQProducer.(*mock.MockRocketMQProducer).EXPECT().
+ SendAsync(gomock.Any(), gomock.Any(), gomock.Any()).Do(
+ func(ctx, mq interface{}, msg ...interface{}) {
+ result := &primitive.SendResult{
+ Status: primitive.SendOK,
+ MsgID: "MockMsgID",
+ MessageQueue: &primitive.MessageQueue{
+ Topic: "TopicTest",
+ },
+ }
+ mq.(func(ctx context.Context, result *primitive.SendResult, err error))(context.Background(), result, nil)
+ },
+ )
+ err := producer.Publish(context.Background(), GetFullEvent(), callback)
+ require.True(t, err == nil)
+ producer.Shutdown()
+}
+
+func TestProducer_SendOneway(t *testing.T) {
+ ctrl := gomock.NewController(t)
+ defer ctrl.Finish()
+ producer := GetProducerWithMockClient(ctrl)
+ producer.Start()
+
+ producer.rocketMQProducer.(*mock.MockRocketMQProducer).EXPECT().
+ SendOneWay(gomock.Any(), gomock.Any()).Do(
+ func(ctx context.Context, mq ...*primitive.Message) {
+ encodedData, _ := datacodec.Encode(context.Background(), event.ApplicationJSON, "{\"data\":\"foo\"}")
+ require.True(t, len(mq) == 1)
+ require.True(t, mq[0].Topic == "TopicTest")
+ require.True(t, string(mq[0].Body) == string(encodedData))
+ },
+ )
+ err := producer.SendOneway(context.Background(), GetFullEvent())
+ require.True(t, err == nil)
+ producer.Shutdown()
+}
+
+func TestProducer_Request(t *testing.T) {
+ ctrl := gomock.NewController(t)
+ defer ctrl.Finish()
+ producer := GetProducerWithMockClient(ctrl)
+ producer.Start()
+
+ callback := &connector.RequestReplyCallback{
+ OnSuccess: func(event *ce.Event) {
+ require.True(t, event.Subject() == "TopicTest")
+ require.True(t, string(event.Data()) == "{\"data\":\"resp\"}")
+ },
+ OnError: func(result *connector.ErrorResult) {
+ panic(result.Err)
+ },
+ }
+
+ producer.rocketMQProducer.(*mock.MockRocketMQProducer).EXPECT().
+ RequestAsync(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Do(
+ func(ctx context.Context, ttl time.Duration,
+ callback func(ctx context.Context, msg *primitive.Message, err error),
+ msg *primitive.Message) {
+ responseMessage := &primitive.Message{
+ Topic: "TopicTest",
+ Body: []byte("{\"data\":\"resp\"}"),
+ }
+ callback(context.Background(), responseMessage, nil)
+ },
+ )
+ err := producer.Request(context.Background(), GetFullEvent(), callback, time.Second)
+ require.True(t, err == nil)
+ producer.Shutdown()
+}
+
+func TestProducer_Reply(t *testing.T) {
+ ctrl := gomock.NewController(t)
+ defer ctrl.Finish()
+ producer := GetProducerWithMockClient(ctrl)
+ producer.Start()
+
+ callback := &connector.SendCallback{
+ OnSuccess: func(result *connector.SendResult) {
+ require.True(t, result.Topic == "TopicTest")
+ require.True(t, result.MessageId == "MockMsgID")
+ },
+ OnError: func(result *connector.ErrorResult) {
+ panic(result.Err)
+ },
+ }
+ producer.rocketMQProducer.(*mock.MockRocketMQProducer).EXPECT().
+ SendAsync(gomock.Any(), gomock.Any(), gomock.Any()).Do(
+ func(ctx, mq interface{}, msg ...interface{}) {
+ result := &primitive.SendResult{
+ Status: primitive.SendOK,
+ MsgID: "MockMsgID",
+ MessageQueue: &primitive.MessageQueue{
+ Topic: "TopicTest",
+ },
+ }
+ mq.(func(ctx context.Context, result *primitive.SendResult, err error))(context.Background(), result, nil)
+ },
+ )
+ err := producer.Reply(context.Background(), GetFullEvent(), callback)
+ require.True(t, err == nil)
+ producer.Shutdown()
+}
+
+func GetFullEvent() *ce.Event {
+ event := ce.NewEvent()
+ event.SetSubject("TopicTest")
+ event.SetID("MockEventId")
+ event.SetDataContentType("application/json")
+ event.SetExtension("protocoltype", "http")
+ event.SetData("application/json", "{\"data\":\"foo\"}")
+ return &event
+}
+
+//func TestProducer_Publish_Broker(t *testing.T) {
+// properties := make(map[string]string)
+// properties["access_points"] = "127.0.0.1:9876"
+// producer := NewProducer()
+// producer.InitProducer(properties)
+// producer.Start()
+//
+// var wg sync.WaitGroup
+// wg.Add(1)
+// callback := &connector.SendCallback{
+// OnSuccess: func(result *connector.SendResult) {
+// require.True(t, result.Topic == "TopicTest")
+// wg.Done()
+// },
+// OnError: func(result *connector.ErrorResult) {
+// panic(result.Err)
+// },
+// }
+//
+// err := producer.Publish(context.Background(), GetFullEvent(), callback)
+// require.True(t, err == nil)
+//
+// wg.Wait()
+// producer.Shutdown()
+//}
diff --git a/plugin/connector/rocketmq/resource.go b/plugin/connector/rocketmq/resource.go
new file mode 100644
index 0000000..b9b39c0
--- /dev/null
+++ b/plugin/connector/rocketmq/resource.go
@@ -0,0 +1,29 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package rocketmq
+
+type Resource struct {
+}
+
+func (r Resource) Init() error {
+ // No-Op for RocketMQ resource
+ return nil
+}
+
+func (r Resource) Release() error {
+ // No-Op for RocketMQ resource
+ return nil
+}
diff --git a/plugin/connector/rocketmq/utils/oms_util.go b/plugin/connector/rocketmq/utils/oms_util.go
new file mode 100644
index 0000000..3d05c11
--- /dev/null
+++ b/plugin/connector/rocketmq/utils/oms_util.go
@@ -0,0 +1,27 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package utils
+
+import (
+ "fmt"
+ "os"
+ "time"
+)
+
+// GetInstanceName build instance name with pid and timestamp
+func GetInstanceName() string {
+ return fmt.Sprintf("%d_EventMesh_%d", os.Getegid(), time.Now().UnixNano())
+}
diff --git a/plugin/connector/standalone/broker.go b/plugin/connector/standalone/broker.go
new file mode 100644
index 0000000..b70f3ba
--- /dev/null
+++ b/plugin/connector/standalone/broker.go
@@ -0,0 +1,259 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package standalone
+
+import (
+ "errors"
+ "fmt"
+ "sync"
+ "time"
+
+ ce "github.com/cloudevents/sdk-go/v2"
+ "go.uber.org/atomic"
+)
+
+const (
+ defaultQueueSize = 1024
+ defaultExpireMills = 60 * 60 * 1000
+)
+
+// MessageQueue message storage of standalone broker
+type MessageQueue struct {
+ capacity int
+ items []*Message
+ mutex sync.Mutex
+ notFull sync.Cond
+ newMsg sync.Cond
+}
+
+func NewMessageQueue() (*MessageQueue, error) {
+ return NewMessageQueueWithCapacity(defaultQueueSize)
+}
+
+func NewMessageQueueWithCapacity(capacity int) (*MessageQueue, error) {
+ if capacity <= 0 {
+ return nil, errors.New("fail to create message queue: capacity must be positive")
+ }
+
+ queue := &MessageQueue{
+ capacity: capacity,
+ items: make([]*Message, 0, capacity),
+ }
+ queue.notFull = sync.Cond{L: &queue.mutex}
+ queue.newMsg = sync.Cond{L: &queue.mutex}
+ return queue, nil
+}
+
+func (q *MessageQueue) Put(message *Message) {
+ q.mutex.Lock()
+ defer q.mutex.Unlock()
+
+ // Wait util queue is not null
+ for q.capacity == len(q.items) {
+ q.notFull.Wait()
+ }
+ q.items = append(q.items, message)
+ q.newMsg.Signal()
+}
+
+// Get fetch message in top of queue, block if message queue is empty
+func (q *MessageQueue) Get() (*Message, error) {
+ q.mutex.Lock()
+ defer q.mutex.Unlock()
+
+ // Wait util queue is not empty
+ for len(q.items) == 0 {
+ q.newMsg.Wait()
+ }
+ return q.items[0], nil
+}
+
+// GetIfNotEmpty fetch message in top of queue, return nil if message queue is empty
+func (q *MessageQueue) GetIfNotEmpty() *Message {
+ q.mutex.Lock()
+ defer q.mutex.Unlock()
+
+ if len(q.items) > 0 {
+ return q.items[0]
+ }
+ return nil
+}
+
+// GetByOffset fetch message of curtain offset
+// return error if message has been deleted, block if message of that offset is not available currently
+func (q *MessageQueue) GetByOffset(offset int64) (*Message, error) {
+ q.mutex.Lock()
+ defer q.mutex.Unlock()
+
+ if offset < 0 {
+ return nil, fmt.Errorf("invalid offset param: %d", offset)
+ }
+
+ for {
+ // Wait util queue is not empty
+ if len(q.items) == 0 {
+ q.newMsg.Wait()
+ continue
+ }
+
+ lastMessage := q.items[len(q.items)-1]
+ // Wait util new message has been put
+ if lastMessage.GetOffset() < offset {
+ q.newMsg.Wait()
+ continue
+ }
+
+ firstMessage := q.items[0]
+ // message has been deleted
+ if firstMessage.GetOffset() > offset {
+ return nil, fmt.Errorf("offset has been deleted, offset : %d", offset)
+ }
+
+ index := int(offset - firstMessage.GetOffset())
+ return q.items[index], nil
+ }
+}
+
+// PopMessage remove message in the top, return nil if message queue is empty
+func (q *MessageQueue) PopMessage() *Message {
+ q.mutex.Lock()
+ defer q.mutex.Unlock()
+ if len(q.items) != 0 {
+ message := q.items[0]
+ q.items = q.items[1:]
+ return message
+ }
+ return nil
+}
+
+func (q *MessageQueue) Size() int {
+ return len(q.items)
+}
+
+// Broker a manager of different topic message queue
+type Broker struct {
+ // <topicName, messageQueue>
+ queueContainer map[string]*MessageQueue
+ // <topicName, offset>
+ offsetContainer map[string]*atomic.Int64
+ // <topicName, worker>
+ expireWorkers map[string]*MessageExpireWorker
+}
+
+var initOnce sync.Once
+var broker *Broker
+
+// GetBroker all topic shared same broker
+func GetBroker() *Broker {
+ initOnce.Do(func() {
+ broker = &Broker{
+ queueContainer: make(map[string]*MessageQueue),
+ offsetContainer: make(map[string]*atomic.Int64),
+ expireWorkers: make(map[string]*MessageExpireWorker),
+ }
+ },
+ )
+ return broker
+}
+
+func (b *Broker) TakeMessage(topicName string) (*Message, error) {
+ if err := b.CreateNewQueueIfAbsent(topicName); err != nil {
+ return nil, err
+ }
+ return b.queueContainer[topicName].Get()
+}
+
+func (b *Broker) TakeMessageByOffset(topicName string, offset int64) (*Message, error) {
+ if err := b.CreateNewQueueIfAbsent(topicName); err != nil {
+ return nil, err
+ }
+ return b.queueContainer[topicName].GetByOffset(offset)
+}
+
+func (b *Broker) PutMessage(topicName string, event *ce.Event) (message *Message, err error) {
+ if err = b.CreateNewQueueIfAbsent(topicName); err != nil {
+ return
+ }
+ message = &Message{
+ createTimeMills: time.Now().UnixMilli(),
+ event: event,
+ }
+ message.SetOffset(b.offsetContainer[topicName].Add(1))
+ b.queueContainer[topicName].Put(message)
+ return
+}
+
+func (b *Broker) CreateNewQueueIfAbsent(topicName string) (err error) {
+ if _, ok := b.queueContainer[topicName]; ok {
+ return
+ }
+ queue, err := NewMessageQueue()
+ if err != nil {
+ return
+ }
+
+ offset := atomic.NewInt64(0)
+ b.queueContainer[topicName] = queue
+ b.offsetContainer[topicName] = offset
+
+ expireWorker := &MessageExpireWorker{
+ messageQueue: queue,
+ quit: make(chan struct{}, 1),
+ expireMills: defaultExpireMills,
+ }
+ b.expireWorkers[topicName] = expireWorker
+ go expireWorker.schedule(5 * time.Second)
+ return
+}
+
+func (b *Broker) ExistTopic(topicName string) (exist bool) {
+ _, exist = b.queueContainer[topicName]
+ return
+}
+
+// MessageExpireWorker periodically evict expire message
+type MessageExpireWorker struct {
+ messageQueue *MessageQueue
+ quit chan struct{}
+ expireMills int64
+}
+
+func (w *MessageExpireWorker) schedule(interval time.Duration) {
+ ticket := time.NewTicker(interval)
+ for {
+ select {
+ case <-ticket.C:
+ w.doEvict()
+ case <-w.quit:
+ ticket.Stop()
+ return
+ }
+ }
+}
+
+func (w *MessageExpireWorker) doEvict() {
+ message := w.messageQueue.GetIfNotEmpty()
+ for message != nil {
+ if message.createTimeMills+w.expireMills < time.Now().UnixMilli() {
+ w.messageQueue.PopMessage()
+ }
+ message = w.messageQueue.GetIfNotEmpty()
+ }
+}
+
+func (w *MessageExpireWorker) stop() {
+ w.quit <- struct{}{}
+}
diff --git a/plugin/connector/standalone/consumer.go b/plugin/connector/standalone/consumer.go
new file mode 100644
index 0000000..2da2b03
--- /dev/null
+++ b/plugin/connector/standalone/consumer.go
@@ -0,0 +1,200 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package standalone
+
+import (
+ "context"
+ "fmt"
+ "sync"
+
+ ce "github.com/cloudevents/sdk-go/v2"
+ "github.com/pkg/errors"
+ "go.uber.org/atomic"
+
+ "github.com/apache/incubator-eventmesh/eventmesh-server-go/log"
+ "github.com/apache/incubator-eventmesh/eventmesh-server-go/plugin/connector"
+)
+
+type Consumer struct {
+ broker *Broker
+ subscribes map[string]*SubscribeWorker
+ committedOffset map[string]*atomic.Int64
+ mutex sync.Mutex
+ listener connector.EventListener
+ started atomic.Bool
+}
+
+func NewConsumer() *Consumer {
+ return &Consumer{
+ broker: GetBroker(),
+ subscribes: make(map[string]*SubscribeWorker),
+ committedOffset: make(map[string]*atomic.Int64),
+ }
+}
+
+func (c *Consumer) Subscribe(topicName string) error {
+ c.mutex.Lock()
+ defer c.mutex.Unlock()
+
+ if c.IsClosed() {
+ return errors.New("fail to subscribe topic, consumer has been closed")
+ }
+
+ if _, ok := c.subscribes[topicName]; !ok {
+ err := c.broker.CreateNewQueueIfAbsent(topicName)
+ if err != nil {
+ return err
+ }
+ offset := atomic.NewInt64(0)
+ worker := &SubscribeWorker{
+ broker: broker,
+ topicName: topicName,
+ offset: offset,
+ listener: c.listener,
+ quit: make(chan struct{}, 1),
+ }
+
+ c.committedOffset[topicName] = offset
+ c.subscribes[topicName] = worker
+ go worker.run()
+ }
+ return nil
+}
+
+func (c *Consumer) Unsubscribe(topicName string) error {
+ c.mutex.Lock()
+ defer c.mutex.Unlock()
+ if worker, ok := c.subscribes[topicName]; ok {
+ delete(c.subscribes, topicName)
+ worker.Stop()
+ }
+ return nil
+}
+
+func (c *Consumer) UpdateOffset(ctx context.Context, events []*ce.Event) error {
+ c.mutex.Lock()
+ defer c.mutex.Unlock()
+ for _, event := range events {
+ topicName := event.Subject()
+ offset := GetOffsetFromEvent(event)
+ if curOffset, ok := c.committedOffset[topicName]; ok {
+ if offset <= 0 {
+ return fmt.Errorf("fail to update offset, invalid param, topic %s, offset : %d", topicName, offset)
+ }
+ if offset < curOffset.Load() {
+ return nil
+ }
+ curOffset.Store(offset)
+ }
+ }
+ return nil
+}
+
+func (c *Consumer) RegisterEventListener(listener *connector.EventListener) {
+ c.listener = *listener
+}
+
+func (c *Consumer) InitConsumer(properties map[string]string) error {
+ // No-Op for standalone connector
+ return nil
+}
+
+func (c *Consumer) Start() error {
+ c.started.CAS(false, true)
+ return nil
+}
+
+func (c *Consumer) Shutdown() error {
+ c.started.CAS(true, false)
+ if ok := c.started.CAS(true, false); ok {
+ for topicName, _ := range c.subscribes {
+ c.Unsubscribe(topicName)
+ delete(c.subscribes, topicName)
+ }
+ }
+ return nil
+}
+
+func (c *Consumer) IsStarted() bool {
+ return c.started.Load()
+}
+
+func (c *Consumer) IsClosed() bool {
+ return !c.started.Load()
+}
+
+// SubscribeWorker pollMessage from topic and manage consume offset
+type SubscribeWorker struct {
+ topicName string
+ broker *Broker
+ listener connector.EventListener
+ offset *atomic.Int64
+ quit chan struct{}
+}
+
+func (w *SubscribeWorker) run() {
+ for {
+ select {
+ case <-w.quit:
+ return
+ default:
+ err := w.pollMessage()
+ if err != nil {
+ // retry
+ log.Error("[Standalone Consumer] fail to poll message from broker, err=%v", err)
+ continue
+ }
+ }
+ }
+}
+
+func (w *SubscribeWorker) pollMessage() error {
+ var message *Message
+ var err error
+ if w.offset.Load() == 0 {
+ message, err = w.broker.TakeMessage(w.topicName)
+ if ok := w.offset.CAS(0, message.GetOffset()); !ok {
+ return nil
+ }
+ } else {
+ message, err = w.broker.TakeMessageByOffset(w.topicName, w.offset.Load()+1)
+ }
+ if err != nil {
+ return errors.Wrap(err, "fail to take message from standalone broker")
+ }
+
+ commitFunc := func(action connector.EventMeshAction) error {
+ switch action {
+ case connector.CommitMessage:
+ // update offset
+ w.offset.Store(message.GetOffset())
+ case connector.ReconsumeLater:
+ // No-Op
+ case connector.ManualAck:
+ // update offset
+ w.offset.Store(message.GetOffset())
+ default:
+ }
+ return nil
+ }
+
+ w.listener.Consume(message.event, commitFunc)
+ return nil
+}
+
+func (w *SubscribeWorker) Stop() {
+ w.quit <- struct{}{}
+}
diff --git a/plugin/connector/standalone/factory.go b/plugin/connector/standalone/factory.go
new file mode 100644
index 0000000..b9b1e5b
--- /dev/null
+++ b/plugin/connector/standalone/factory.go
@@ -0,0 +1,70 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package standalone
+
+import (
+ "errors"
+ "github.com/apache/incubator-eventmesh/eventmesh-server-go/plugin"
+ "github.com/apache/incubator-eventmesh/eventmesh-server-go/plugin/connector"
+)
+
+func init() {
+ plugin.Register("standalone", &Factory{})
+}
+
+type Factory struct {
+ plugin.Plugin
+ properties map[string]string
+ consumer connector.Consumer
+ producer connector.Producer
+}
+
+func (f *Factory) Type() string {
+ return connector.PluginType
+}
+
+func (f *Factory) Setup(name string, dec plugin.Decoder) error {
+ if dec == nil {
+ return errors.New("standalone config decoder empty")
+ }
+ properties := make(map[string]string)
+ if err := dec.Decode(properties); err != nil {
+ return err
+ }
+ f.properties = properties
+ consumer := NewConsumer()
+ consumer.InitConsumer(f.properties)
+ consumer.Start()
+ f.consumer = consumer
+
+ producer := NewProducer()
+ producer.InitProducer(f.properties)
+ producer.Start()
+ f.producer = producer
+ return nil
+}
+
+func (f *Factory) GetConsumer() (connector.Consumer, error) {
+ return f.consumer, nil
+}
+
+func (f *Factory) GetProducer() (connector.Producer, error) {
+ return f.producer, nil
+}
+
+func (f *Factory) GetResource() (connector.Resource, error) {
+ return &Resource{}, nil
+}
diff --git a/plugin/connector/standalone/message.go b/plugin/connector/standalone/message.go
new file mode 100644
index 0000000..943bb03
--- /dev/null
+++ b/plugin/connector/standalone/message.go
@@ -0,0 +1,47 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package standalone
+
+import (
+ ce "github.com/cloudevents/sdk-go/v2"
+ "strconv"
+)
+
+type Message struct {
+ createTimeMills int64
+ event *ce.Event
+}
+
+func (m *Message) GetTopicName() string {
+ return m.event.Subject()
+}
+
+func (m *Message) GetOffset() int64 {
+ return GetOffsetFromEvent(m.event)
+}
+
+// SetOffset since the CloudEvents go-sdk doesn't support int64 in v1 version, need to store offset by string
+func (m *Message) SetOffset(offset int64) {
+ m.event.SetExtension("offset", strconv.FormatInt(offset, 10))
+}
+
+func GetOffsetFromEvent(event *ce.Event) int64 {
+ offset, err := strconv.ParseInt(event.Extensions()["offset"].(string), 10, 64)
+ if err != nil {
+ panic(err)
+ }
+ return offset
+}
diff --git a/plugin/connector/standalone/producer.go b/plugin/connector/standalone/producer.go
new file mode 100644
index 0000000..2d07e6e
--- /dev/null
+++ b/plugin/connector/standalone/producer.go
@@ -0,0 +1,116 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package standalone
+
+import (
+ "context"
+ "errors"
+ "fmt"
+ "strconv"
+ "time"
+
+ ce "github.com/cloudevents/sdk-go/v2"
+ "go.uber.org/atomic"
+
+ "github.com/apache/incubator-eventmesh/eventmesh-server-go/plugin/connector"
+)
+
+// Producer standalone producer
+type Producer struct {
+ broker *Broker
+ started *atomic.Bool
+}
+
+func NewProducer() *Producer {
+ return &Producer{
+ broker: GetBroker(),
+ started: atomic.NewBool(false),
+ }
+}
+
+func (p *Producer) Publish(ctx context.Context, event *ce.Event, callback *connector.SendCallback) (err error) {
+ defer func() {
+ if r := recover(); r != nil {
+ err = fmt.Errorf("callback function execute failed: %v", err)
+ }
+ }()
+
+ if p.IsClosed() {
+ err = errors.New("fail to publish message, producer has been closed")
+ return
+ }
+
+ message, err := p.broker.PutMessage(event.Subject(), event)
+ if err != nil {
+ callback.OnError(&connector.ErrorResult{
+ Topic: event.Subject(),
+ Err: err,
+ })
+ return
+ }
+ sendResult := connector.SendResult{
+ MessageId: strconv.FormatInt(message.GetOffset(), 10),
+ Topic: event.Subject(),
+ Err: nil,
+ }
+ callback.OnSuccess(&sendResult)
+ return
+}
+
+func (p *Producer) SendOneway(ctx context.Context, event *ce.Event) (err error) {
+ _, err = p.broker.PutMessage(event.Subject(), event)
+ return
+}
+
+func (p *Producer) Request(ctx context.Context, event *ce.Event, callback *connector.RequestReplyCallback, timeout time.Duration) error {
+ return fmt.Errorf("request is not supported in standalone connector")
+}
+
+func (p *Producer) Reply(ctx context.Context, event *ce.Event, callback *connector.SendCallback) error {
+ return fmt.Errorf("reply is not supported in standalone connector")
+}
+
+func (p *Producer) CheckTopicExist(topicName string) (exist bool, err error) {
+ return p.broker.ExistTopic(topicName), nil
+}
+
+func (p *Producer) SetExtFields() error {
+ // No-Op for standalone producer
+ return nil
+}
+
+func (p *Producer) InitProducer(properties map[string]string) error {
+ // No-Op for standalone producer
+ return nil
+}
+
+func (p *Producer) Start() error {
+ p.started.CAS(false, true)
+ return nil
+}
+
+func (p *Producer) Shutdown() error {
+ p.started.CAS(true, false)
+ return nil
+}
+
+func (p *Producer) IsStarted() bool {
+ return p.started.Load()
+}
+
+func (p *Producer) IsClosed() bool {
+ return !p.started.Load()
+}
diff --git a/plugin/connector/standalone/resource.go b/plugin/connector/standalone/resource.go
new file mode 100644
index 0000000..7ddcfe7
--- /dev/null
+++ b/plugin/connector/standalone/resource.go
@@ -0,0 +1,29 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package standalone
+
+type Resource struct {
+}
+
+func (r Resource) Init() error {
+ // No-Op for standalone resource
+ return nil
+}
+
+func (r Resource) Release() error {
+ // No-Op for standalone resource
+ return nil
+}
diff --git a/plugin/connector/standalone/standalone_connector_test.go b/plugin/connector/standalone/standalone_connector_test.go
new file mode 100644
index 0000000..c6f70b2
--- /dev/null
+++ b/plugin/connector/standalone/standalone_connector_test.go
@@ -0,0 +1,224 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package standalone
+
+import (
+ "context"
+ "fmt"
+ "github.com/apache/incubator-eventmesh/eventmesh-server-go/plugin"
+ "github.com/apache/incubator-eventmesh/eventmesh-server-go/plugin/connector"
+ ce "github.com/cloudevents/sdk-go/v2"
+ "github.com/google/uuid"
+ "github.com/stretchr/testify/assert"
+ "gopkg.in/yaml.v3"
+ "testing"
+)
+
+const (
+ topicName = "example-topic"
+ pluginName = "standalone"
+)
+
+// MockDecoder standalone connector properties mock decoder
+type MockDecoder struct {
+}
+
+// Decode mock decoder, no-op
+func (m *MockDecoder) Decode(cfg interface{}) error {
+ return nil
+}
+
+func TestProducer_Publish(t *testing.T) {
+ factory := &Factory{}
+ err := factory.Setup(pluginName, &plugin.YamlNodeDecoder{
+ Node: &yaml.Node{},
+ })
+ assert.NoError(t, err)
+ producer, _ := factory.GetProducer()
+ producer.Start()
+ defer producer.Shutdown()
+
+ var publishSuccess bool
+ var callBackErr error
+ topic := fmt.Sprintf("%s_publish", topicName)
+ callback := connector.SendCallback{
+ OnSuccess: func(result *connector.SendResult) {
+ publishSuccess = true
+ assert.Equal(t, topic, result.Topic)
+ assert.Equal(t, "1", result.MessageId)
+ assert.Nil(t, result.Err)
+ },
+ OnError: func(result *connector.ErrorResult) {
+ callBackErr = result.Err
+ },
+ }
+
+ err = producer.Publish(context.Background(), getTestEvent(topic), &callback)
+ assert.Nil(t, err)
+ assert.True(t, publishSuccess)
+ assert.Nil(t, callBackErr)
+
+ exist, err := producer.CheckTopicExist(topic)
+ assert.True(t, exist)
+ assert.Nil(t, err)
+
+}
+func TestConsumer_Subscribe(t *testing.T) {
+ done := make(chan struct{})
+ topic := fmt.Sprintf("%s_subscribe", topicName)
+ listener := connector.EventListener{
+ Consume: func(event *ce.Event, commitFunc connector.CommitFunc) error {
+ var data map[string]interface{}
+ event.DataAs(&data)
+ t.Log(event.String())
+ commitFunc(connector.CommitMessage)
+ done <- struct{}{}
+ return nil
+ },
+ }
+
+ factory := &Factory{}
+ err := factory.Setup(pluginName, &plugin.YamlNodeDecoder{
+ Node: &yaml.Node{},
+ })
+ assert.NoError(t, err)
+ consumer, _ := factory.GetConsumer()
+ consumer.Start()
+ consumer.RegisterEventListener(&listener)
+ consumer.Subscribe(topic)
+ defer consumer.Shutdown()
+
+ producer, _ := factory.GetProducer()
+ producer.Start()
+ defer producer.Shutdown()
+ err = producer.Publish(context.Background(), getTestEventOfData(topic, map[string]interface{}{
+ "val": "value",
+ }), getEmptyPublishCallback())
+ assert.NoError(t, err)
+ <-done
+}
+
+func TestConsumer_ManualAck(t *testing.T) {
+ done := make(chan struct{})
+ topic := fmt.Sprintf("%s_ack", topicName)
+ listener := connector.EventListener{
+ Consume: func(event *ce.Event, commitFunc connector.CommitFunc) error {
+ var data map[string]interface{}
+ event.DataAs(&data)
+ commitFunc(connector.ManualAck)
+ done <- struct{}{}
+ return nil
+ },
+ }
+
+ factory := &Factory{}
+ err := factory.Setup(pluginName, &plugin.YamlNodeDecoder{
+ Node: &yaml.Node{},
+ })
+ assert.NoError(t, err)
+ consumer, _ := factory.GetConsumer()
+ consumer.Start()
+ consumer.RegisterEventListener(&listener)
+ consumer.Subscribe(topic)
+ defer consumer.Shutdown()
+
+ producer, _ := factory.GetProducer()
+ producer.Start()
+ defer producer.Shutdown()
+ err = producer.Publish(context.Background(), getTestEventOfData(topic, map[string]interface{}{
+ "val": "test",
+ }), getEmptyPublishCallback())
+ assert.NoError(t, err)
+ <-done
+}
+
+// TODO update later
+func TestConsumer_UpdateOffset(t *testing.T) {
+ //sum := atomic.NewInt64(0)
+ //ch := make(chan struct{})
+ //listener := connector.EventListener{
+ // Consume: func(event *ce.Event, commitFunc connector.CommitFunc) error {
+ // var data map[string]interface{}
+ // event.DataAs(&data)
+ // sum.Add(int64(data["val"].(float64)))
+ // commitFunc(connector.CommitMessage)
+ // ch <- struct{}{}
+ // return nil
+ // },
+ //}
+ //
+ //factory := &Factory{}
+ //err := factory.Setup(pluginName, &plugin.YamlNodeDecoder{
+ // Node: &yaml.Node{},
+ //})
+ //assert.NoError(t, err)
+ //consumer, _ := factory.GetConsumer()
+ //consumer.Start()
+ //defer consumer.Shutdown()
+ //consumer.RegisterEventListener(&listener)
+ //event := getTestEvent()
+ //event.SetExtension("offset", "49")
+ //consumer.Subscribe(topicName)
+ //consumer.UpdateOffset(context.Background(), []*ce.Event{event})
+ //
+ //producer, _ := factory.GetProducer()
+ //producer.Start()
+ //defer producer.Shutdown()
+ //for i := 1; i <= 50; i++ {
+ // err := producer.Publish(context.Background(), getTestEventOfData(map[string]interface{}{
+ // "val": i,
+ // }), getEmptyPublishCallback())
+ //
+ // if err != nil {
+ // t.Fail()
+ // return
+ // }
+ //}
+ //
+ //timer := time.NewTimer(3 * time.Second)
+ //select {
+ //case <-timer.C:
+ // t.Fail()
+ //case <-ch:
+ // assert.Equal(t, int64(50), sum.Load())
+ //}
+}
+
+func getTestEvent(topicName string) *ce.Event {
+ event := ce.NewEvent()
+ event.SetID(uuid.New().String())
+ event.SetSubject(topicName)
+ return &event
+}
+
+func getTestEventOfData(topicName string, data map[string]interface{}) *ce.Event {
+ event := ce.NewEvent()
+ event.SetID(uuid.New().String())
+ event.SetSubject(topicName)
+ event.SetData(ce.ApplicationJSON, data)
+ return &event
+}
+
+func getEmptyPublishCallback() *connector.SendCallback {
+ return &connector.SendCallback{
+ OnSuccess: func(result *connector.SendResult) {
+ // No-Op
+ },
+ OnError: func(result *connector.ErrorResult) {
+ panic(result.Err)
+ },
+ }
+}
diff --git a/plugin/database/mysql/mysql.go b/plugin/database/mysql/mysql.go
new file mode 100644
index 0000000..ca88462
--- /dev/null
+++ b/plugin/database/mysql/mysql.go
@@ -0,0 +1,68 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package mysql
+
+import (
+ "github.com/apache/incubator-eventmesh/eventmesh-server-go/plugin"
+)
+
+const (
+ PluginType = "database"
+ PluginName = "mysql"
+)
+
+const (
+ defaultDriverName = "mysql"
+)
+
+// PluginConfig global mysql plugin configuration
+var PluginConfig Config
+
+func init() {
+ plugin.Register(PluginName, &MysqlPlugin{})
+}
+
+// Config mysql proxy config
+type Config struct {
+ DSN string `yaml:"dsn"`
+ MaxIdle int `yaml:"max_idle"`
+ MaxOpen int `yaml:"max_open"`
+ MaxLifetime int `yaml:"max_lifetime"`
+ DriverName string `yaml:"driver_name"`
+}
+
+func (c *Config) setDefault() {
+ if c.DriverName == "" {
+ c.DriverName = defaultDriverName
+ }
+}
+
+// MysqlPlugin load mysql plugin configuration
+type MysqlPlugin struct{}
+
+// Type plugin type
+func (m *MysqlPlugin) Type() string {
+ return PluginType
+}
+
+// Setup setup config mysql connect configuration (integration with gorm)
+func (m *MysqlPlugin) Setup(name string, configDesc plugin.Decoder) (err error) {
+ if err = configDesc.Decode(&PluginConfig); err != nil {
+ return
+ }
+ PluginConfig.setDefault()
+ return nil
+}
diff --git a/plugin/metrics/metrics.go b/plugin/metrics/metrics.go
new file mode 100644
index 0000000..3184e92
--- /dev/null
+++ b/plugin/metrics/metrics.go
@@ -0,0 +1,36 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package metrics
+
+import "github.com/apache/incubator-eventmesh/eventmesh-server-go/plugin"
+
+const (
+ PluginType = "metrics"
+)
+
+// Metrics plugin interface
+type Metrics interface {
+ plugin.Plugin
+
+ // IncCount inc count by name and label
+ IncCount(name string, label string)
+
+ // AddCount add count by name and label
+ AddCount(name string, label string, val float64)
+
+ // RecordLatency record latency by name and label
+ RecordLatency(name string, label string, latency float64)
+}
diff --git a/plugin/metrics/prometheus/config.go b/plugin/metrics/prometheus/config.go
new file mode 100644
index 0000000..03137a5
--- /dev/null
+++ b/plugin/metrics/prometheus/config.go
@@ -0,0 +1,26 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package prometheus
+
+type Config struct {
+ EndpointPort string `yaml:"endpoint_port"`
+}
+
+func NewDefaultConfig() *Config {
+ return &Config{
+ EndpointPort: "19090",
+ }
+}
diff --git a/plugin/metrics/prometheus/metrics.go b/plugin/metrics/prometheus/metrics.go
new file mode 100644
index 0000000..0febf7c
--- /dev/null
+++ b/plugin/metrics/prometheus/metrics.go
@@ -0,0 +1,153 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package prometheus
+
+import (
+ "fmt"
+ "github.com/apache/incubator-eventmesh/eventmesh-server-go/plugin"
+ "github.com/apache/incubator-eventmesh/eventmesh-server-go/plugin/metrics"
+ "github.com/prometheus/client_golang/prometheus"
+ "github.com/prometheus/client_golang/prometheus/promhttp"
+ "net/http"
+ "sync"
+)
+
+func init() {
+ plugin.Register("prometheus", getDefaultMetrics())
+}
+
+func getDefaultMetrics() *Metrics {
+ return &Metrics{
+ counters: make(map[string]prometheus.Collector),
+ histograms: make(map[string]prometheus.Collector)}
+}
+
+type Metrics struct {
+ counters map[string]prometheus.Collector
+ histograms map[string]prometheus.Collector
+ lock sync.Mutex
+ once sync.Once
+ config *Config
+}
+
+const nameSpace = "eventmesh"
+const subSystem = "runtime"
+
+const (
+ histogram = iota
+ counter
+)
+
+func (p *Metrics) Type() string {
+ return metrics.PluginType
+}
+
+func (p *Metrics) Setup(name string, dec plugin.Decoder) error {
+ config := NewDefaultConfig()
+ err := dec.Decode(config)
+ if err != nil {
+ return err
+ }
+ p.Init(config)
+ return nil
+}
+
+// Init try to init metrics, include exposing http endpoint
+func (p *Metrics) Init(config *Config) {
+ p.once.Do(func() {
+ p.config = config
+ p.exposeEndpoint()
+ })
+}
+
+// exposeEndpoint expose http endpoint
+func (p *Metrics) exposeEndpoint() {
+ go func() {
+ http.Handle("/metrics", promhttp.Handler())
+ http.ListenAndServe(fmt.Sprintf(":%s", p.config.EndpointPort), nil)
+ }()
+}
+
+// loadCollector load collector by name and collectorType
+func (p *Metrics) loadCollector(name string, collectorType int) prometheus.Collector {
+ if collector := p.getCollectorByNameAndType(name, collectorType); collector != nil {
+ return collector
+ }
+ return p.registerNewCollector(name, collectorType)
+}
+
+func (p *Metrics) getCollectorByNameAndType(name string, collectorType int) prometheus.Collector {
+ switch collectorType {
+ case counter:
+ return p.counters[name]
+ case histogram:
+ return p.histograms[name]
+ default:
+ panic("prometheus metrics plugin get collector error, illegal collector type")
+ }
+}
+
+// registerNewCollector create and register new collector of collectorType
+func (p *Metrics) registerNewCollector(name string, collectorType int) prometheus.Collector {
+ p.lock.Lock()
+ defer p.lock.Unlock()
+
+ if collector := p.getCollectorByNameAndType(name, collectorType); collector != nil {
+ return collector
+ }
+
+ var collector prometheus.Collector
+ switch collectorType {
+ case counter:
+ collector = prometheus.NewCounterVec(
+ prometheus.CounterOpts{
+ Namespace: nameSpace,
+ Subsystem: subSystem,
+ Name: name,
+ }, []string{"label"},
+ )
+ p.counters[name] = collector
+ case histogram:
+ collector = prometheus.NewHistogramVec(
+ prometheus.HistogramOpts{
+ Namespace: nameSpace,
+ Subsystem: subSystem,
+ Name: name,
+ Buckets: prometheus.ExponentialBuckets(1, 2, 13),
+ }, []string{"label"},
+ )
+ p.histograms[name] = collector
+ default:
+ panic("prometheus metrics plugin register collector error, illegal collector type")
+ }
+ prometheus.MustRegister(collector)
+ return collector
+}
+
+func (p *Metrics) IncCount(name string, label string) {
+ collector := p.loadCollector(name, counter).(*prometheus.CounterVec)
+ collector.With(prometheus.Labels{"label": label}).Inc()
+}
+
+func (p *Metrics) AddCount(name string, label string, val float64) {
+ collector := p.loadCollector(name, counter).(*prometheus.CounterVec)
+ collector.With(prometheus.Labels{"label": label}).Add(val)
+}
+
+func (p *Metrics) RecordLatency(name string, label string, latency float64) {
+ collector := p.loadCollector(name, histogram).(*prometheus.HistogramVec)
+ collector.With(prometheus.Labels{"label": label}).Observe(latency)
+}
diff --git a/plugin/metrics/prometheus/metrics_test.go b/plugin/metrics/prometheus/metrics_test.go
new file mode 100644
index 0000000..5aa7ac5
--- /dev/null
+++ b/plugin/metrics/prometheus/metrics_test.go
@@ -0,0 +1,30 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package prometheus
+
+import (
+ "github.com/apache/incubator-eventmesh/eventmesh-server-go/plugin"
+ mtr "github.com/apache/incubator-eventmesh/eventmesh-server-go/plugin/metrics"
+ "testing"
+)
+
+func TestCounterAndLatency(t *testing.T) {
+ metrics := plugin.Get("metrics", "prometheus").(mtr.Metrics)
+ metrics.(*Metrics).Init(NewDefaultConfig())
+ metrics.IncCount("eventmesh_tcp_retry_queue_size", "")
+ metrics.AddCount("eventmesh_tcp_connection_num", "", 10)
+ metrics.RecordLatency("eventmesh_http_request_cost", "", 1000)
+}
diff --git a/plugin/plugin.go b/plugin/plugin.go
new file mode 100644
index 0000000..9cb0605
--- /dev/null
+++ b/plugin/plugin.go
@@ -0,0 +1,66 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Package plugin implements a general plugin factory system which provides plugin registration and loading.
+// It is mainly used when certain plugins must be loaded by configuration.
+// This system is not supposed to register plugins that do not rely on configuration like codec. Instead, plugins
+// that do not rely on configuration should be registered by calling methods in certain packages.
+package plugin
+
+var plugins = make(map[string]map[string]Plugin) // plugin type => { plugin name => plugin factory }
+
+var activePlugins = make(map[string]string) // store the active plugins{ plugin type => plugin name}
+
+// Plugin is the interface for plugin factory abstraction.
+// Custom Plugins need to implement this interface to be registered as a plugin with certain type.
+type Plugin interface {
+ // Type returns type of the plugin, i.e. selector, log, config, tracing.
+ Type() string
+ // Setup loads plugin by configuration.
+ // The data structure of the configuration of the plugin needs to be defined in advance。
+ Setup(name string, dec Decoder) error
+}
+
+// Decoder is the interface used to decode plugin configuration.
+type Decoder interface {
+ Decode(cfg interface{}) error // the input param is the custom configuration of the plugin
+}
+
+// Register registers a plugin factory.
+// Name of the plugin should be specified.
+// It is supported to register instances which are the same implementation of plugin Plugin
+// but use different configuration.
+func Register(name string, f Plugin) {
+ factories, ok := plugins[f.Type()]
+ if !ok {
+ factories = make(map[string]Plugin)
+ plugins[f.Type()] = factories
+ }
+ factories[name] = f
+}
+
+func SetActivePlugin(ap map[string]string) {
+ activePlugins = ap
+}
+
+// Get returns a plugin Plugin by its type and name.
+func Get(typ string, name string) Plugin {
+ return plugins[typ][name]
+}
+
+// GetByType return the single plugin
+func GetByType(typ string) Plugin {
+ return Get(typ, activePlugins[typ])
+}
diff --git a/plugin/protocol/cloudevents/Readme.md b/plugin/protocol/cloudevents/Readme.md
new file mode 100644
index 0000000..6f2089a
--- /dev/null
+++ b/plugin/protocol/cloudevents/Readme.md
@@ -0,0 +1 @@
+transfer the message with cloudevents protocol
\ No newline at end of file
diff --git a/plugin/protocol/cloudevents/cloudevents.go b/plugin/protocol/cloudevents/cloudevents.go
new file mode 100644
index 0000000..f1a52ca
--- /dev/null
+++ b/plugin/protocol/cloudevents/cloudevents.go
@@ -0,0 +1,215 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package cloudevents
+
+import (
+ "context"
+ "fmt"
+ "github.com/apache/incubator-eventmesh/eventmesh-server-go/pkg/common/protocol/grpc"
+ "github.com/apache/incubator-eventmesh/eventmesh-server-go/pkg/common/protocol/tcp"
+ "github.com/apache/incubator-eventmesh/eventmesh-server-go/plugin"
+ "github.com/apache/incubator-eventmesh/eventmesh-server-go/plugin/protocol"
+ "github.com/apache/incubator-eventmesh/eventmesh-server-go/runtime/consts"
+ "github.com/apache/incubator-eventmesh/eventmesh-server-go/runtime/proto/pb"
+ "github.com/cloudevents/sdk-go/v2"
+ "github.com/cloudevents/sdk-go/v2/event"
+ "github.com/cloudevents/sdk-go/v2/event/datacodec"
+)
+
+func init() {
+ plugin.Register(protocol.CloudEvents, &CloudeventsPlugin{})
+}
+
+// CloudeventsPlugin CloudEvents protocol adaptor
+// used to transform CloudEvents message to CloudEvents message.
+type CloudeventsPlugin struct {
+}
+
+func (c *CloudeventsPlugin) Type() string {
+ return plugin.Protocol
+}
+
+func (c *CloudeventsPlugin) Setup(name string, dec plugin.Decoder) error {
+ return nil
+}
+
+func (c *CloudeventsPlugin) ToCloudEvent(msg interface{}) (*v2.Event, error) {
+ switch msg.(type) {
+ case *tcp.Package:
+ pck := msg.(*tcp.Package)
+ return c.deserializeTcpProtocol(pck)
+ case *grpc.SimpleMessageWrapper:
+ sw := msg.(*grpc.SimpleMessageWrapper)
+ sm := sw.SimpleMessage
+ return deserializeGrpcProtocol(sm)
+ }
+ panic("implement me")
+}
+
+func (c *CloudeventsPlugin) ToCloudEvents(msg interface{}) ([]*v2.Event, error) {
+ bmw := msg.(*grpc.BatchMessageWrapper)
+ return buildBatchMessage(bmw.BatchMessage)
+}
+
+func (c *CloudeventsPlugin) FromCloudEvent(event *v2.Event) (interface{}, error) {
+ desc := event.Extensions()[grpc.PROTOCOL_DESC].(string)
+ if desc == "grpc" {
+ return buildSimpleMessage(event)
+ }
+ return nil, fmt.Errorf("only grpc supported now")
+}
+
+func (c *CloudeventsPlugin) ProtocolType() string {
+ //TODO implement me
+ panic("implement me")
+}
+
+func (c *CloudeventsPlugin) deserializeTcpProtocol(pck *tcp.Package) (*v2.Event, error) {
+ // TODO add when support tcp procotol
+ panic("implement me")
+}
+
+func defaultIfEmpty(in string, def interface{}) string {
+ if in == "" {
+ return def.(string)
+ }
+ return in
+}
+
+func defaultIfNil(def string, in interface{}) string {
+ if in == nil {
+ return def
+ }
+ return in.(string)
+}
+
+func deserializeGrpcProtocol(sm *pb.SimpleMessage) (*v2.Event, error) {
+ content := sm.Content
+ ct, ok := sm.Properties[grpc.CONTENT_TYPE]
+ if !ok {
+ ct = consts.CONTENT_TYPE_CLOUDEVENTS_JSON
+ }
+ evt := v2.NewEvent()
+ if err := datacodec.Decode(context.TODO(), ct, []byte(content), &evt); err != nil {
+ return nil, err
+ }
+ hdr := sm.Header
+ result := v2.NewEvent()
+ ver := defaultIfEmpty(hdr.ProtocolVersion, evt.Extensions()[grpc.PROTOCOL_VERSION])
+ topic := defaultIfEmpty(sm.Topic, evt.Subject())
+ result.SetExtension(grpc.ENV, defaultIfEmpty(hdr.Env, evt.Extensions()[grpc.ENV]))
+ result.SetExtension(grpc.IDC, defaultIfEmpty(hdr.Idc, evt.Extensions()[grpc.IDC]))
+ result.SetExtension(grpc.IP, defaultIfEmpty(hdr.Ip, evt.Extensions()[grpc.IP]))
+ result.SetExtension(grpc.PID, defaultIfEmpty(hdr.Pid, evt.Extensions()[grpc.PID]))
+ result.SetExtension(grpc.SYS, defaultIfEmpty(hdr.Sys, evt.Extensions()[grpc.SYS]))
+ result.SetExtension(grpc.LANGUAGE, defaultIfEmpty(hdr.Language, evt.Extensions()[grpc.LANGUAGE]))
+ result.SetExtension(grpc.PROTOCOL_TYPE, defaultIfEmpty(hdr.ProtocolType, evt.Extensions()[grpc.PROTOCOL_TYPE]))
+ result.SetExtension(grpc.PROTOCOL_DESC, defaultIfEmpty(hdr.ProtocolDesc, evt.Extensions()[grpc.PROTOCOL_DESC]))
+ result.SetExtension(grpc.PROTOCOL_VERSION, defaultIfEmpty(hdr.ProtocolVersion, evt.Extensions()[grpc.PROTOCOL_VERSION]))
+ result.SetExtension(grpc.UNIQUE_ID, defaultIfEmpty(sm.UniqueId, evt.Extensions()[grpc.UNIQUE_ID]))
+ result.SetExtension(grpc.SEQ_NUM, defaultIfEmpty(sm.SeqNum, evt.Extensions()[grpc.SEQ_NUM]))
+ result.SetExtension(grpc.USERNAME, defaultIfEmpty(hdr.Username, evt.Extensions()[grpc.USERNAME]))
+ result.SetExtension(grpc.PASSWD, defaultIfEmpty(hdr.Password, evt.Extensions()[grpc.PASSWD]))
+ result.SetExtension(grpc.TTL, defaultIfEmpty(sm.Ttl, evt.Extensions()[grpc.TTL]))
+ result.SetExtension(grpc.PRODUCERGROUP, defaultIfEmpty(sm.ProducerGroup, evt.Extensions()[grpc.PRODUCERGROUP]))
+
+ if ver == event.CloudEventsVersionV1 {
+ result.SetSpecVersion(event.CloudEventsVersionV1)
+ } else {
+ result.SetSpecVersion(event.CloudEventsVersionV03)
+ }
+ result.SetSubject(topic)
+
+ return &result, nil
+}
+
+func buildBatchMessage(bm *pb.BatchMessage) ([]*v2.Event, error) {
+ var msgs []*v2.Event
+ hdr := bm.Header
+
+ for _, item := range bm.MessageItem {
+ content := item.Content
+ ct, ok := item.Properties[grpc.CONTENT_TYPE]
+ if !ok {
+ ct = consts.CONTENT_TYPE_CLOUDEVENTS_JSON
+ }
+ evt := v2.NewEvent()
+ if err := datacodec.Decode(context.TODO(), ct, []byte(content), &evt); err != nil {
+ return nil, err
+ }
+ result := v2.NewEvent()
+ ver := defaultIfEmpty(hdr.ProtocolVersion, evt.Extensions()[grpc.PROTOCOL_VERSION])
+ topic := defaultIfEmpty(bm.Topic, evt.Subject())
+ result.SetExtension(grpc.ENV, defaultIfEmpty(hdr.Env, evt.Extensions()[grpc.ENV]))
+ result.SetExtension(grpc.IDC, defaultIfEmpty(hdr.Idc, evt.Extensions()[grpc.IDC]))
+ result.SetExtension(grpc.IP, defaultIfEmpty(hdr.Ip, evt.Extensions()[grpc.IP]))
+ result.SetExtension(grpc.PID, defaultIfEmpty(hdr.Pid, evt.Extensions()[grpc.PID]))
+ result.SetExtension(grpc.SYS, defaultIfEmpty(hdr.Sys, evt.Extensions()[grpc.SYS]))
+ result.SetExtension(grpc.LANGUAGE, defaultIfEmpty(hdr.Language, evt.Extensions()[grpc.LANGUAGE]))
+ result.SetExtension(grpc.PROTOCOL_TYPE, defaultIfEmpty(hdr.ProtocolType, evt.Extensions()[grpc.PROTOCOL_TYPE]))
+ result.SetExtension(grpc.PROTOCOL_DESC, defaultIfEmpty(hdr.ProtocolDesc, evt.Extensions()[grpc.PROTOCOL_DESC]))
+ result.SetExtension(grpc.PROTOCOL_VERSION, defaultIfEmpty(hdr.ProtocolVersion, evt.Extensions()[grpc.PROTOCOL_VERSION]))
+ result.SetExtension(grpc.UNIQUE_ID, defaultIfEmpty(item.UniqueId, evt.Extensions()[grpc.UNIQUE_ID]))
+ result.SetExtension(grpc.SEQ_NUM, defaultIfEmpty(item.SeqNum, evt.Extensions()[grpc.SEQ_NUM]))
+ result.SetExtension(grpc.USERNAME, defaultIfEmpty(hdr.Username, evt.Extensions()[grpc.USERNAME]))
+ result.SetExtension(grpc.PASSWD, defaultIfEmpty(hdr.Password, evt.Extensions()[grpc.PASSWD]))
+ result.SetExtension(grpc.TTL, defaultIfEmpty(item.Ttl, evt.Extensions()[grpc.TTL]))
+ result.SetExtension(grpc.PRODUCERGROUP, defaultIfEmpty(bm.ProducerGroup, evt.Extensions()[grpc.PRODUCERGROUP]))
+ if ver == event.CloudEventsVersionV1 {
+ result.SetSpecVersion(event.CloudEventsVersionV1)
+ } else {
+ result.SetSpecVersion(event.CloudEventsVersionV03)
+ }
+ result.SetSubject(topic)
+ msgs = append(msgs, &result)
+ }
+ return msgs, nil
+}
+
+func buildSimpleMessage(evt *v2.Event) (*grpc.SimpleMessageWrapper, error) {
+ ct, err := datacodec.Encode(context.TODO(), evt.DataContentType(), evt)
+ if err != nil {
+ return nil, err
+ }
+ hdr := &pb.RequestHeader{
+ Env: defaultIfNil("env", evt.Extensions()[grpc.ENV]),
+ Idc: defaultIfNil("idc", evt.Extensions()[grpc.IDC]),
+ Ip: defaultIfNil("127.0.0.1", evt.Extensions()[grpc.IP]),
+ Pid: defaultIfNil("123", evt.Extensions()[grpc.PID]),
+ Sys: defaultIfNil("sys123", evt.Extensions()[grpc.SYS]),
+ Username: defaultIfNil("user", evt.Extensions()[grpc.USERNAME]),
+ Password: defaultIfNil("pass", evt.Extensions()[grpc.PASSWD]),
+ Language: defaultIfNil("JAVA", evt.Extensions()[grpc.LANGUAGE]),
+ ProtocolType: defaultIfNil("protocol", evt.Extensions()[grpc.PROTOCOL_TYPE]),
+ ProtocolDesc: defaultIfNil("protocolDesc", evt.Extensions()[grpc.PROTOCOL_DESC]),
+ ProtocolVersion: defaultIfNil("1.0", evt.Extensions()[grpc.PROTOCOL_VERSION]),
+ }
+ msg := &pb.SimpleMessage{
+ Header: hdr,
+ Content: string(ct),
+ ProducerGroup: defaultIfNil("producerGroup", evt.Extensions()[grpc.PRODUCERGROUP]),
+ SeqNum: defaultIfNil("", evt.Extensions()[grpc.SEQ_NUM]),
+ UniqueId: defaultIfNil("", evt.Extensions()[grpc.UNIQUE_ID]),
+ Topic: evt.Subject(),
+ Ttl: defaultIfNil("3000", evt.Extensions()[grpc.TTL]),
+ Properties: map[string]string{grpc.CONTENT_TYPE: evt.DataContentType()},
+ }
+ for k, v := range evt.Extensions() {
+ msg.Properties[k] = defaultIfNil("", v)
+ }
+
+ return &grpc.SimpleMessageWrapper{SimpleMessage: msg}, nil
+}
diff --git a/plugin/protocol/protocol.go b/plugin/protocol/protocol.go
new file mode 100644
index 0000000..e06b35c
--- /dev/null
+++ b/plugin/protocol/protocol.go
@@ -0,0 +1,40 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package protocol
+
+import (
+ cloudv2 "github.com/cloudevents/sdk-go/v2"
+)
+
+var (
+ CloudEvents = "cloudevents"
+)
+
+// Adapter protocol adapter
+// transfer the message with given protocol and events message
+type Adapter interface {
+ // ToCloudEvent transform protocol to CloudEvent
+ ToCloudEvent(interface{}) (*cloudv2.Event, error)
+
+ //ToCloudEvents transform protocol to CloudEvent list.
+ ToCloudEvents(interface{}) ([]*cloudv2.Event, error)
+
+ //FromCloudEvent transfor CloudEvent to target protocol.
+ FromCloudEvent(event *cloudv2.Event) (interface{}, error)
+
+ // ProtocolType protocol type, protocol type should not be null
+ ProtocolType() string
+}
diff --git a/plugin/registry/model.go b/plugin/registry/model.go
new file mode 100644
index 0000000..4953312
--- /dev/null
+++ b/plugin/registry/model.go
@@ -0,0 +1,42 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package registry
+
+import "time"
+
+type EventMeshDataInfo struct {
+ EventMeshClusterName string
+ EventMeshName string
+ Endpoint string
+ LastUpdateTimestamp time.Time
+ Metadata map[string]string
+}
+
+type EventMeshRegisterInfo struct {
+ EventMeshClusterName string
+ EventMeshName string
+ EndPoint string
+ EventMeshInstanceNumMap map[string]map[string]int
+ Metadata map[string]string
+ ProtocolType string
+}
+
+type EventMeshUnRegisterInfo struct {
+ EventMeshClusterName string
+ EventMeshName string
+ EndPoint string
+ ProtocolType string
+}
diff --git a/plugin/registry/nacos/config.go b/plugin/registry/nacos/config.go
new file mode 100644
index 0000000..51a9574
--- /dev/null
+++ b/plugin/registry/nacos/config.go
@@ -0,0 +1,24 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package nacos
+
+// Config define registry nacos plugin config
+type Config struct {
+ ServiceName string `yaml:"service-name"`
+ CacheDir string `yaml:"cache-dir"`
+ Port string `yaml:"port"` // nacos server port
+ AddressList string `yaml:"address-list"` // nacos server address list
+}
diff --git a/plugin/registry/nacos/mocks/mock_naming_client_interface.go b/plugin/registry/nacos/mocks/mock_naming_client_interface.go
new file mode 100644
index 0000000..84082c4
--- /dev/null
+++ b/plugin/registry/nacos/mocks/mock_naming_client_interface.go
@@ -0,0 +1,211 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Code generated by MockGen. DO NOT EDIT.
+// Source: ./naming_client_interface.go
+
+// Package mocks is a generated GoMock package.
+package mocks
+
+import (
+ reflect "reflect"
+
+ gomock "github.com/golang/mock/gomock"
+ model "github.com/nacos-group/nacos-sdk-go/v2/model"
+ vo "github.com/nacos-group/nacos-sdk-go/v2/vo"
+)
+
+// MockINamingClient is a mock of INamingClient interface.
+type MockINamingClient struct {
+ ctrl *gomock.Controller
+ recorder *MockINamingClientMockRecorder
+}
+
+// MockINamingClientMockRecorder is the mock recorder for MockINamingClient.
+type MockINamingClientMockRecorder struct {
+ mock *MockINamingClient
+}
+
+// NewMockINamingClient creates a new mock instance.
+func NewMockINamingClient(ctrl *gomock.Controller) *MockINamingClient {
+ mock := &MockINamingClient{ctrl: ctrl}
+ mock.recorder = &MockINamingClientMockRecorder{mock}
+ return mock
+}
+
+// EXPECT returns an object that allows the caller to indicate expected use.
+func (m *MockINamingClient) EXPECT() *MockINamingClientMockRecorder {
+ return m.recorder
+}
+
+// CloseClient mocks base method.
+func (m *MockINamingClient) CloseClient() {
+ m.ctrl.T.Helper()
+ m.ctrl.Call(m, "CloseClient")
+}
+
+// CloseClient indicates an expected call of CloseClient.
+func (mr *MockINamingClientMockRecorder) CloseClient() *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CloseClient", reflect.TypeOf((*MockINamingClient)(nil).CloseClient))
+}
+
+// DeregisterInstance mocks base method.
+func (m *MockINamingClient) DeregisterInstance(param vo.DeregisterInstanceParam) (bool, error) {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "DeregisterInstance", param)
+ ret0, _ := ret[0].(bool)
+ ret1, _ := ret[1].(error)
+ return ret0, ret1
+}
+
+// DeregisterInstance indicates an expected call of DeregisterInstance.
+func (mr *MockINamingClientMockRecorder) DeregisterInstance(param interface{}) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeregisterInstance", reflect.TypeOf((*MockINamingClient)(nil).DeregisterInstance), param)
+}
+
+// GetAllServicesInfo mocks base method.
+func (m *MockINamingClient) GetAllServicesInfo(param vo.GetAllServiceInfoParam) (model.ServiceList, error) {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "GetAllServicesInfo", param)
+ ret0, _ := ret[0].(model.ServiceList)
+ ret1, _ := ret[1].(error)
+ return ret0, ret1
+}
+
+// GetAllServicesInfo indicates an expected call of GetAllServicesInfo.
+func (mr *MockINamingClientMockRecorder) GetAllServicesInfo(param interface{}) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAllServicesInfo", reflect.TypeOf((*MockINamingClient)(nil).GetAllServicesInfo), param)
+}
+
+// GetService mocks base method.
+func (m *MockINamingClient) GetService(param vo.GetServiceParam) (model.Service, error) {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "GetService", param)
+ ret0, _ := ret[0].(model.Service)
+ ret1, _ := ret[1].(error)
+ return ret0, ret1
+}
+
+// GetService indicates an expected call of GetService.
+func (mr *MockINamingClientMockRecorder) GetService(param interface{}) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetService", reflect.TypeOf((*MockINamingClient)(nil).GetService), param)
+}
+
+// RegisterInstance mocks base method.
+func (m *MockINamingClient) RegisterInstance(param vo.RegisterInstanceParam) (bool, error) {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "RegisterInstance", param)
+ ret0, _ := ret[0].(bool)
+ ret1, _ := ret[1].(error)
+ return ret0, ret1
+}
+
+// RegisterInstance indicates an expected call of RegisterInstance.
+func (mr *MockINamingClientMockRecorder) RegisterInstance(param interface{}) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RegisterInstance", reflect.TypeOf((*MockINamingClient)(nil).RegisterInstance), param)
+}
+
+// SelectAllInstances mocks base method.
+func (m *MockINamingClient) SelectAllInstances(param vo.SelectAllInstancesParam) ([]model.Instance, error) {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "SelectAllInstances", param)
+ ret0, _ := ret[0].([]model.Instance)
+ ret1, _ := ret[1].(error)
+ return ret0, ret1
+}
+
+// SelectAllInstances indicates an expected call of SelectAllInstances.
+func (mr *MockINamingClientMockRecorder) SelectAllInstances(param interface{}) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectAllInstances", reflect.TypeOf((*MockINamingClient)(nil).SelectAllInstances), param)
+}
+
+// SelectInstances mocks base method.
+func (m *MockINamingClient) SelectInstances(param vo.SelectInstancesParam) ([]model.Instance, error) {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "SelectInstances", param)
+ ret0, _ := ret[0].([]model.Instance)
+ ret1, _ := ret[1].(error)
+ return ret0, ret1
+}
+
+// SelectInstances indicates an expected call of SelectInstances.
+func (mr *MockINamingClientMockRecorder) SelectInstances(param interface{}) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectInstances", reflect.TypeOf((*MockINamingClient)(nil).SelectInstances), param)
+}
+
+// SelectOneHealthyInstance mocks base method.
+func (m *MockINamingClient) SelectOneHealthyInstance(param vo.SelectOneHealthInstanceParam) (*model.Instance, error) {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "SelectOneHealthyInstance", param)
+ ret0, _ := ret[0].(*model.Instance)
+ ret1, _ := ret[1].(error)
+ return ret0, ret1
+}
+
+// SelectOneHealthyInstance indicates an expected call of SelectOneHealthyInstance.
+func (mr *MockINamingClientMockRecorder) SelectOneHealthyInstance(param interface{}) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectOneHealthyInstance", reflect.TypeOf((*MockINamingClient)(nil).SelectOneHealthyInstance), param)
+}
+
+// Subscribe mocks base method.
+func (m *MockINamingClient) Subscribe(param *vo.SubscribeParam) error {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "Subscribe", param)
+ ret0, _ := ret[0].(error)
+ return ret0
+}
+
+// Subscribe indicates an expected call of Subscribe.
+func (mr *MockINamingClientMockRecorder) Subscribe(param interface{}) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Subscribe", reflect.TypeOf((*MockINamingClient)(nil).Subscribe), param)
+}
+
+// Unsubscribe mocks base method.
+func (m *MockINamingClient) Unsubscribe(param *vo.SubscribeParam) error {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "Unsubscribe", param)
+ ret0, _ := ret[0].(error)
+ return ret0
+}
+
+// Unsubscribe indicates an expected call of Unsubscribe.
+func (mr *MockINamingClientMockRecorder) Unsubscribe(param interface{}) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Unsubscribe", reflect.TypeOf((*MockINamingClient)(nil).Unsubscribe), param)
+}
+
+// UpdateInstance mocks base method.
+func (m *MockINamingClient) UpdateInstance(param vo.UpdateInstanceParam) (bool, error) {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "UpdateInstance", param)
+ ret0, _ := ret[0].(bool)
+ ret1, _ := ret[1].(error)
+ return ret0, ret1
+}
+
+// UpdateInstance indicates an expected call of UpdateInstance.
+func (mr *MockINamingClientMockRecorder) UpdateInstance(param interface{}) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateInstance", reflect.TypeOf((*MockINamingClient)(nil).UpdateInstance), param)
+}
diff --git a/plugin/registry/nacos/nacos.go b/plugin/registry/nacos/nacos.go
new file mode 100644
index 0000000..052fc19
--- /dev/null
+++ b/plugin/registry/nacos/nacos.go
@@ -0,0 +1,241 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package nacos
+
+import (
+ "fmt"
+ "github.com/apache/incubator-eventmesh/eventmesh-server-go/config"
+ "github.com/apache/incubator-eventmesh/eventmesh-server-go/plugin"
+ "github.com/apache/incubator-eventmesh/eventmesh-server-go/plugin/registry"
+ "github.com/gogf/gf/util/gconv"
+ "github.com/nacos-group/nacos-sdk-go/v2/clients"
+ "github.com/nacos-group/nacos-sdk-go/v2/clients/naming_client"
+ "github.com/nacos-group/nacos-sdk-go/v2/common/constant"
+ "github.com/nacos-group/nacos-sdk-go/v2/vo"
+ "go.uber.org/atomic"
+ "net"
+ "strconv"
+ "strings"
+ "sync"
+ "time"
+)
+
+var (
+ defaultConnectTimeout = 5000 * time.Millisecond
+ DefaultClusterName = "DEFAULT"
+ DefaultGroupName = "DEFAULT_GROUP"
+ DefaultWeight float64 = 10
+ protoList = []string{"TCP", "HTTP", "GRPC"}
+)
+
+func init() {
+ plugin.Register("nacos", &Registry{})
+}
+
+type Registry struct {
+ initStatus *atomic.Bool
+ startStatus *atomic.Bool
+ cfg *Config
+ client naming_client.INamingClient
+ registryInfos *sync.Map // map[string]*registry.EventMeshRegisterInfo
+}
+
+func (r *Registry) Type() string {
+ return registry.Type
+}
+
+func (r *Registry) Setup(name string, dec plugin.Decoder) error {
+ conf := &Config{}
+ if err := dec.Decode(conf); err != nil {
+ return err
+ }
+ r.cfg = conf
+ return nil
+}
+
+func (r *Registry) Init() error {
+ r.initStatus = atomic.NewBool(true)
+ r.startStatus = atomic.NewBool(false)
+ r.registryInfos = new(sync.Map)
+ return nil
+}
+
+func (r *Registry) Start() error {
+ var p vo.NacosClientParam
+ var addresses []string
+
+ if len(r.cfg.AddressList) > 0 {
+ addresses = strings.Split(r.cfg.AddressList, ",")
+ }
+
+ for _, address := range addresses {
+ ip, port, err := net.SplitHostPort(address)
+ if err != nil {
+ return err
+ }
+ p.ServerConfigs = append(p.ServerConfigs, constant.ServerConfig{IpAddr: ip, Port: gconv.Uint64(port)})
+ }
+
+ p.ClientConfig = &constant.ClientConfig{
+ TimeoutMs: uint64(defaultConnectTimeout.Milliseconds()),
+ CacheDir: r.cfg.CacheDir,
+ }
+ cli, err := clients.NewNamingClient(p)
+ if err != nil {
+ return err
+ }
+
+ r.startStatus.Store(true)
+ r.client = cli
+ return nil
+}
+
+func (r *Registry) Shutdown() error {
+ r.startStatus.Store(false)
+ r.initStatus.Store(false)
+ r.client.CloseClient()
+ return nil
+}
+
+func (r *Registry) FindEventMeshInfoByCluster(clusterName string) ([]*registry.EventMeshDataInfo, error) {
+ var infos []*registry.EventMeshDataInfo
+ meshServerName := config.GlobalConfig().Common.Name
+ cluster := config.GlobalConfig().Common.Cluster
+ for _, proto := range protoList {
+ ins, err := r.client.SelectInstances(vo.SelectInstancesParam{
+ Clusters: []string{clusterName},
+ ServiceName: fmt.Sprintf("%v-%v", meshServerName, proto),
+ GroupName: cluster,
+ HealthyOnly: true,
+ })
+ if err != nil {
+ return nil, err
+ }
+ if len(ins) == 0 {
+ continue
+ }
+ for _, in := range ins {
+ infos = append(infos, ®istry.EventMeshDataInfo{
+ EventMeshClusterName: in.ClusterName,
+ EventMeshName: in.ServiceName,
+ Endpoint: fmt.Sprintf("%v:%v", in.Ip, in.Port),
+ LastUpdateTimestamp: time.Time{},
+ Metadata: in.Metadata,
+ })
+ }
+ }
+
+ return infos, nil
+}
+
+func (r *Registry) FindAllEventMeshInfo() ([]*registry.EventMeshDataInfo, error) {
+ var infos []*registry.EventMeshDataInfo
+ meshServerName := config.GlobalConfig().Common.Name
+
+ for _, proto := range protoList {
+ ins, err := r.client.SelectInstances(vo.SelectInstancesParam{
+ Clusters: []string{},
+ ServiceName: fmt.Sprintf("%v-%v", meshServerName, proto),
+ GroupName: "GROUP",
+ HealthyOnly: true,
+ })
+ if err != nil {
+ return nil, err
+ }
+ if len(ins) == 0 {
+ continue
+ }
+ for _, in := range ins {
+ infos = append(infos, ®istry.EventMeshDataInfo{
+ EventMeshClusterName: in.ClusterName,
+ EventMeshName: in.ServiceName,
+ Endpoint: fmt.Sprintf("%v:%v", in.Ip, in.Port),
+ LastUpdateTimestamp: time.Time{},
+ Metadata: in.Metadata,
+ })
+ }
+ }
+
+ return infos, nil
+}
+
+// FindEventMeshClientDistributionData not used
+// deprecate
+func (r *Registry) FindEventMeshClientDistributionData(clusterName, group, purpose string) (map[string]map[string]int, error) {
+ return nil, nil
+}
+
+func (r *Registry) RegisterMetadata(map[string]string) {
+ r.registryInfos.Range(func(key, value any) bool {
+ r.Register(value.(*registry.EventMeshRegisterInfo))
+ return true
+ })
+}
+
+func (r *Registry) Register(info *registry.EventMeshRegisterInfo) error {
+ ipPort := strings.Split(info.EndPoint, ":")
+ if len(ipPort) != 2 {
+ return fmt.Errorf("endpoint format err")
+ }
+ ip := ipPort[0]
+ port, err := strconv.ParseInt(ipPort[1], 10, 64)
+ if err != nil {
+ return err
+ }
+
+ _, err = r.client.RegisterInstance(vo.RegisterInstanceParam{
+ Ip: ip,
+ Port: uint64(port),
+ ServiceName: info.EventMeshName,
+ GroupName: uniqGroupName(info.ProtocolType),
+ Healthy: true,
+ Enable: true,
+ Weight: DefaultWeight,
+ })
+ if err != nil {
+ return err
+ }
+ r.registryInfos.Store(info.EventMeshName, info)
+ return nil
+}
+
+func (r *Registry) UnRegister(info *registry.EventMeshUnRegisterInfo) error {
+ ipPort := strings.Split(info.EndPoint, ":")
+ if len(ipPort) != 2 {
+ return fmt.Errorf("endpoint format err")
+ }
+ ip := ipPort[0]
+ port, err := strconv.ParseInt(ipPort[1], 10, 64)
+ if err != nil {
+ return err
+ }
+
+ _, err = r.client.DeregisterInstance(vo.DeregisterInstanceParam{
+ Ip: ip,
+ Port: uint64(port),
+ ServiceName: info.EventMeshName,
+ GroupName: uniqGroupName(info.ProtocolType),
+ })
+ if err != nil {
+ return err
+ }
+ r.registryInfos.Delete(info.EventMeshName)
+ return nil
+}
+
+func uniqGroupName(protoType string) string {
+ return fmt.Sprintf("%s-GROUP", protoType)
+}
diff --git a/plugin/registry/nacos/nacos_test.go b/plugin/registry/nacos/nacos_test.go
new file mode 100644
index 0000000..fa4aff3
--- /dev/null
+++ b/plugin/registry/nacos/nacos_test.go
@@ -0,0 +1,314 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package nacos
+
+import (
+ "fmt"
+ "github.com/nacos-group/nacos-sdk-go/v2/model"
+ "os"
+ "path/filepath"
+ "testing"
+
+ "github.com/golang/mock/gomock"
+ "github.com/nacos-group/nacos-sdk-go/v2/vo"
+ "github.com/stretchr/testify/assert"
+
+ "github.com/apache/incubator-eventmesh/eventmesh-server-go/plugin/registry"
+ "github.com/apache/incubator-eventmesh/eventmesh-server-go/plugin/registry/nacos/mocks"
+)
+
+func Test_Init(t *testing.T) {
+ r := &Registry{}
+ assert.NoError(t, r.Init())
+ assert.True(t, r.initStatus.Load())
+ assert.False(t, r.startStatus.Load())
+}
+
+func Test_Start(t *testing.T) {
+ t.Run("create nameing client", func(t *testing.T) {
+ cacheDir := filepath.Join(os.TempDir(), "nacos-test-cache-dir")
+ assert.NoError(t, os.MkdirAll(cacheDir, os.ModePerm))
+ defer os.RemoveAll(cacheDir)
+
+ r := &Registry{
+ cfg: &Config{
+ ServiceName: "test",
+ CacheDir: "/tmp/",
+ Port: "8088",
+ AddressList: "127.0.0.1:8081",
+ },
+ }
+ assert.NoError(t, r.Init())
+ ctrl := gomock.NewController(t)
+ mocks.NewMockINamingClient(ctrl)
+ assert.NoError(t, r.Start())
+ assert.NoError(t, r.Shutdown())
+ assert.False(t, r.initStatus.Load())
+ assert.False(t, r.startStatus.Load())
+ })
+}
+
+func Test_Register(t *testing.T) {
+ cacheDir := filepath.Join(os.TempDir(), "nacos-test-cache-dir")
+ assert.NoError(t, os.MkdirAll(cacheDir, os.ModePerm))
+ defer os.RemoveAll(cacheDir)
+
+ r := &Registry{
+ cfg: &Config{
+ ServiceName: "test",
+ CacheDir: "/tmp/",
+ Port: "8088",
+ AddressList: "127.0.0.1:8081",
+ },
+ }
+ assert.NoError(t, r.Init())
+ assert.True(t, r.initStatus.Load())
+ assert.False(t, r.startStatus.Load())
+ assert.NoError(t, r.Start())
+ ctrl := gomock.NewController(t)
+ nameclient := mocks.NewMockINamingClient(ctrl)
+ meshInfo := ®istry.EventMeshRegisterInfo{
+ EventMeshClusterName: "test",
+ EventMeshName: "test",
+ EndPoint: "127.0.0.1:3333",
+ EventMeshInstanceNumMap: map[string]map[string]int{},
+ Metadata: map[string]string{},
+ ProtocolType: "GRPC",
+ }
+ nameclient.EXPECT().RegisterInstance(vo.RegisterInstanceParam{
+ Ip: "127.0.0.1",
+ Port: 3333,
+ ServiceName: "test",
+ GroupName: uniqGroupName("GRPC"),
+ Healthy: true,
+ Enable: true,
+ Weight: DefaultWeight,
+ }).Return(true, nil).Times(1)
+ r.client = nameclient
+ err := r.Register(meshInfo)
+ assert.NoError(t, err)
+ val, ok := r.registryInfos.Load(meshInfo.EventMeshName)
+ assert.True(t, ok)
+ assert.NotNil(t, val)
+}
+
+func Test_DeRegister(t *testing.T) {
+ cacheDir := filepath.Join(os.TempDir(), "nacos-test-cache-dir")
+ assert.NoError(t, os.MkdirAll(cacheDir, os.ModePerm))
+ defer os.RemoveAll(cacheDir)
+
+ r := &Registry{
+ cfg: &Config{
+ ServiceName: "test",
+ CacheDir: "/tmp/",
+ Port: "8088",
+ AddressList: "127.0.0.1:8081",
+ },
+ }
+ assert.NoError(t, r.Init())
+ assert.True(t, r.initStatus.Load())
+ assert.False(t, r.startStatus.Load())
+ assert.NoError(t, r.Start())
+ ctrl := gomock.NewController(t)
+ nameclient := mocks.NewMockINamingClient(ctrl)
+ meshInfo := ®istry.EventMeshRegisterInfo{
+ EventMeshClusterName: "test",
+ EventMeshName: "test",
+ EndPoint: "127.0.0.1:3333",
+ EventMeshInstanceNumMap: map[string]map[string]int{},
+ Metadata: map[string]string{},
+ ProtocolType: "GRPC",
+ }
+ nameclient.EXPECT().RegisterInstance(vo.RegisterInstanceParam{
+ Ip: "127.0.0.1",
+ Port: 3333,
+ ServiceName: "test",
+ GroupName: uniqGroupName("GRPC"),
+ Healthy: true,
+ Enable: true,
+ Weight: DefaultWeight,
+ }).Return(true, nil).Times(1)
+ r.client = nameclient
+ err := r.Register(meshInfo)
+ assert.NoError(t, err)
+ val, ok := r.registryInfos.Load(meshInfo.EventMeshName)
+ assert.True(t, ok)
+ assert.NotNil(t, val)
+
+ unmeshInfo := ®istry.EventMeshUnRegisterInfo{
+ EventMeshClusterName: "test",
+ EventMeshName: "test",
+ EndPoint: "127.0.0.1:3333",
+ ProtocolType: "GRPC",
+ }
+ nameclient.EXPECT().DeregisterInstance(vo.DeregisterInstanceParam{
+ Ip: "127.0.0.1",
+ Port: 3333,
+ ServiceName: meshInfo.EventMeshName,
+ GroupName: uniqGroupName("GRPC"),
+ }).Return(true, nil).Times(1)
+ err = r.UnRegister(unmeshInfo)
+ assert.NoError(t, err)
+ val, ok = r.registryInfos.Load(meshInfo.EventMeshName)
+ assert.False(t, ok)
+ assert.Nil(t, val)
+}
+
+func Test_FindEventMeshInfoByCluster(t *testing.T) {
+ cacheDir := filepath.Join(os.TempDir(), "nacos-test-cache-dir")
+ assert.NoError(t, os.MkdirAll(cacheDir, os.ModePerm))
+ defer os.RemoveAll(cacheDir)
+
+ r := &Registry{
+ cfg: &Config{
+ ServiceName: "test",
+ CacheDir: "/tmp/",
+ Port: "8088",
+ AddressList: "127.0.0.1:8081",
+ },
+ }
+ assert.NoError(t, r.Init())
+ assert.True(t, r.initStatus.Load())
+ assert.False(t, r.startStatus.Load())
+ assert.NoError(t, r.Start())
+
+ t.Run("return empty", func(t *testing.T) {
+ ctrl := gomock.NewController(t)
+ nameclient := mocks.NewMockINamingClient(ctrl)
+ r.client = nameclient
+ protoList = []string{"GRPC"}
+ nameclient.EXPECT().SelectInstances(vo.SelectInstancesParam{
+ Clusters: []string{"test"},
+ ServiceName: fmt.Sprintf("%v-%v", "eventmesh-server", "GRPC"),
+ GroupName: "1",
+ HealthyOnly: true,
+ }).Return([]model.Instance{}, nil).AnyTimes()
+ val, err := r.FindEventMeshInfoByCluster("test")
+ assert.NoError(t, err)
+ assert.Equal(t, len(val), 0)
+ })
+
+ t.Run("return 1 instance", func(t *testing.T) {
+ ctrl := gomock.NewController(t)
+ nameclient := mocks.NewMockINamingClient(ctrl)
+ r.client = nameclient
+ protoList = []string{"GRPC"}
+ nameclient.EXPECT().SelectInstances(vo.SelectInstancesParam{
+ Clusters: []string{"test"},
+ ServiceName: fmt.Sprintf("%v-%v", "eventmesh-server", "GRPC"),
+ GroupName: "1",
+ HealthyOnly: true,
+ }).Return([]model.Instance{
+ {
+ InstanceId: "1",
+ },
+ }, nil).AnyTimes()
+ val, err := r.FindEventMeshInfoByCluster("test")
+ assert.NoError(t, err)
+ assert.Equal(t, len(val), 1)
+ })
+
+ t.Run("return err", func(t *testing.T) {
+ ctrl := gomock.NewController(t)
+ nameclient := mocks.NewMockINamingClient(ctrl)
+ r.client = nameclient
+ protoList = []string{"GRPC"}
+ mockErr := fmt.Errorf("mock err")
+ nameclient.EXPECT().SelectInstances(vo.SelectInstancesParam{
+ Clusters: []string{"test"},
+ ServiceName: fmt.Sprintf("%v-%v", "eventmesh-server", "GRPC"),
+ GroupName: "1",
+ HealthyOnly: true,
+ }).Return(nil, mockErr).AnyTimes()
+ val, err := r.FindEventMeshInfoByCluster("test")
+ assert.Error(t, err)
+ assert.Equal(t, err, mockErr)
+ assert.Nil(t, val)
+ })
+}
+
+func Test_FindAllEventMeshInfo(t *testing.T) {
+ cacheDir := filepath.Join(os.TempDir(), "nacos-test-cache-dir")
+ assert.NoError(t, os.MkdirAll(cacheDir, os.ModePerm))
+ defer os.RemoveAll(cacheDir)
+
+ r := &Registry{
+ cfg: &Config{
+ ServiceName: "test",
+ CacheDir: "/tmp/",
+ Port: "8088",
+ AddressList: "127.0.0.1:8081",
+ },
+ }
+ assert.NoError(t, r.Init())
+ assert.True(t, r.initStatus.Load())
+ assert.False(t, r.startStatus.Load())
+ assert.NoError(t, r.Start())
+
+ t.Run("return empty", func(t *testing.T) {
+ ctrl := gomock.NewController(t)
+ nameclient := mocks.NewMockINamingClient(ctrl)
+ r.client = nameclient
+ protoList = []string{"GRPC"}
+ nameclient.EXPECT().SelectInstances(vo.SelectInstancesParam{
+ Clusters: []string{},
+ ServiceName: fmt.Sprintf("%v-%v", "eventmesh-server", "GRPC"),
+ GroupName: "GROUP",
+ HealthyOnly: true,
+ }).Return([]model.Instance{}, nil).AnyTimes()
+ val, err := r.FindAllEventMeshInfo()
+ assert.NoError(t, err)
+ assert.Equal(t, len(val), 0)
+ })
+
+ t.Run("return 1 instance", func(t *testing.T) {
+ ctrl := gomock.NewController(t)
+ nameclient := mocks.NewMockINamingClient(ctrl)
+ r.client = nameclient
+ protoList = []string{"GRPC"}
+ nameclient.EXPECT().SelectInstances(vo.SelectInstancesParam{
+ Clusters: []string{},
+ ServiceName: fmt.Sprintf("%v-%v", "eventmesh-server", "GRPC"),
+ GroupName: "GROUP",
+ HealthyOnly: true,
+ }).Return([]model.Instance{
+ {
+ InstanceId: "1",
+ },
+ }, nil).AnyTimes()
+ val, err := r.FindAllEventMeshInfo()
+ assert.NoError(t, err)
+ assert.Equal(t, len(val), 1)
+ })
+
+ t.Run("return err", func(t *testing.T) {
+ ctrl := gomock.NewController(t)
+ nameclient := mocks.NewMockINamingClient(ctrl)
+ r.client = nameclient
+ protoList = []string{"GRPC"}
+ mockErr := fmt.Errorf("mock err")
+ nameclient.EXPECT().SelectInstances(vo.SelectInstancesParam{
+ Clusters: []string{},
+ ServiceName: fmt.Sprintf("%v-%v", "eventmesh-server", "GRPC"),
+ GroupName: "GROUP",
+ HealthyOnly: true,
+ }).Return(nil, mockErr).AnyTimes()
+ val, err := r.FindAllEventMeshInfo()
+ assert.Error(t, err)
+ assert.Equal(t, err, mockErr)
+ assert.Nil(t, val)
+ })
+}
diff --git a/plugin/registry/registry.go b/plugin/registry/registry.go
new file mode 100644
index 0000000..4b6f5d5
--- /dev/null
+++ b/plugin/registry/registry.go
@@ -0,0 +1,30 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package registry
+
+var Type = "registry"
+
+type Interface interface {
+ Init() error
+ Start() error
+ Shutdown() error
+ FindEventMeshInfoByCluster(clusterName string) ([]*EventMeshDataInfo, error)
+ FindAllEventMeshInfo() ([]*EventMeshDataInfo, error)
+ FindEventMeshClientDistributionData(clusterName, group, purpose string) (map[string]map[string]int, error)
+ RegisterMetadata(map[string]string)
+ Register(info *EventMeshRegisterInfo) error
+ UnRegister(info *EventMeshUnRegisterInfo) error
+}
diff --git a/plugin/setup.go b/plugin/setup.go
new file mode 100644
index 0000000..cfe1468
--- /dev/null
+++ b/plugin/setup.go
@@ -0,0 +1,153 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// This file shows how to load plugins through a yaml config file.
+
+package plugin
+
+import (
+ "errors"
+ "fmt"
+ "time"
+
+ yaml "gopkg.in/yaml.v3"
+)
+
+var (
+ // SetupTimeout is the timeout for initialization of each plugin.
+ // Modify it if some plugins' initialization does take a long time.
+ SetupTimeout = 10 * time.Second
+
+ // MaxPluginSize is the max number of plugins.
+ MaxPluginSize = 1000
+)
+
+// Config is the configuration of all plugins. plugin type => { plugin name => plugin config }
+type Config map[string]map[string]yaml.Node
+
+// Setup loads plugins by configuration.
+func (c Config) Setup() error {
+ // load plugins one by one through the config file and put them into an ordered plugin queue.
+ plugins, status, err := c.loadPlugins()
+ if err != nil {
+ return err
+ }
+
+ // remove and setup plugins one by one from the front of the ordered plugin queue.
+ if _, err := c.setupPlugins(plugins, status); err != nil {
+ return err
+ }
+ return nil
+}
+
+func (c Config) loadPlugins() (chan pluginInfo, map[string]bool, error) {
+ var (
+ plugins = make(chan pluginInfo, MaxPluginSize) // use channel as plugin queue
+ // plugins' status. plugin key => {true: init done, false: init not done}.
+ status = make(map[string]bool)
+ )
+ for typ, factories := range c {
+ for name, cfg := range factories {
+ factory := Get(typ, name)
+ if factory == nil {
+ return nil, nil, fmt.Errorf("plugin %s:%s no registered or imported, do not configure", typ, name)
+ }
+ p := pluginInfo{
+ factory: factory,
+ typ: typ,
+ name: name,
+ cfg: cfg,
+ }
+ select {
+ case plugins <- p:
+ default:
+ return nil, nil, fmt.Errorf("plugin number exceed max limit:%d", len(plugins))
+ }
+ status[p.key()] = false
+ }
+ }
+ return plugins, status, nil
+}
+
+func (c Config) setupPlugins(plugins chan pluginInfo, status map[string]bool) ([]pluginInfo, error) {
+ var (
+ result []pluginInfo
+ num = len(plugins)
+ )
+ for num > 0 {
+ for i := 0; i < num; i++ {
+ p := <-plugins
+ if err := p.setup(); err != nil {
+ return nil, err
+ }
+ status[p.key()] = true
+ result = append(result, p)
+ }
+ if len(plugins) == num { // none plugin is setup, circular dependency exists.
+ return nil, fmt.Errorf("cycle depends, not plugin is setup")
+ }
+ num = len(plugins) // continue to process plugins that were moved to tail of the channel.
+ }
+ return result, nil
+}
+
+// ------------------------------------------------------------------------------------- //
+
+// pluginInfo is the information of a plugin.
+type pluginInfo struct {
+ factory Plugin
+ typ string
+ name string
+ cfg yaml.Node
+}
+
+// setup initializes a single plugin.
+func (p *pluginInfo) setup() error {
+ var (
+ ch = make(chan struct{})
+ err error
+ )
+ go func() {
+ err = p.factory.Setup(p.name, &YamlNodeDecoder{Node: &p.cfg})
+ close(ch)
+ }()
+ select {
+ case <-ch:
+ case <-time.After(SetupTimeout):
+ return fmt.Errorf("setup plugin %s timeout", p.key())
+ }
+ if err != nil {
+ return fmt.Errorf("setup plugin %s error: %v", p.key(), err)
+ }
+ return nil
+}
+
+// YamlNodeDecoder is a decoder for a yaml.Node of the yaml config file.
+type YamlNodeDecoder struct {
+ Node *yaml.Node
+}
+
+// Decode decodes a yaml.Node of the yaml config file.
+func (d *YamlNodeDecoder) Decode(cfg interface{}) error {
+ if d.Node == nil {
+ return errors.New("yaml node empty")
+ }
+ return d.Node.Decode(cfg)
+}
+
+// key returns the unique index of plugin in the format of 'type-name'.
+func (p *pluginInfo) key() string {
+ return p.typ + "-" + p.name
+}
diff --git a/plugin/types.go b/plugin/types.go
new file mode 100644
index 0000000..14982ed
--- /dev/null
+++ b/plugin/types.go
@@ -0,0 +1,37 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more
... 9112 lines suppressed ...
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@eventmesh.apache.org
For additional commands, e-mail: commits-help@eventmesh.apache.org
[eventmesh-go] 22/26: update ci.yml
Posted by ch...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
chenguangsheng pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/eventmesh-go.git
commit bcfa3d89654614075b9911d2890b050888f6a88f
Author: Alonexc <91...@users.noreply.github.com>
AuthorDate: Mon Apr 24 17:08:20 2023 +0800
update ci.yml
---
.github/workflows/ci.yml | 4 ----
1 file changed, 4 deletions(-)
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 845851e..e10746d 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -57,7 +57,3 @@ jobs:
uses: apache/skywalking-eyes@main
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
-
- - name: Check third party dependencies
- run: |
- ./gradlew clean jar dist -x test -x checkstyleMain -x javaDoc && ./gradlew installPlugin && ./gradlew tar && sh tools/dependency-check/check-dependencies.sh && echo "Thirty party dependencies check success"
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@eventmesh.apache.org
For additional commands, e-mail: commits-help@eventmesh.apache.org
[eventmesh-go] 13/26: Merge pull request #7 from harshithasudhakar/patch-1
Posted by ch...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
chenguangsheng pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/eventmesh-go.git
commit be275b7902bbe65869df72f6a90c94596852e040
Merge: 6cedded 904020c
Author: walleliu <li...@163.com>
AuthorDate: Sun Apr 23 18:51:52 2023 -0700
Merge pull request #7 from harshithasudhakar/patch-1
[ISSUE #3769]should use time.Until instead of t.Sub(time.Now())[retry.go]
runtime/core/protocol/grpc/retry/retry.go | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@eventmesh.apache.org
For additional commands, e-mail: commits-help@eventmesh.apache.org
[eventmesh-go] 05/26: Update .asf.yaml
Posted by ch...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
chenguangsheng pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/eventmesh-go.git
commit 0f19952aec8d3ec20442d9bda0316a313a53129d
Author: Eason Chen <qq...@gmail.com>
AuthorDate: Wed Apr 19 10:29:16 2023 +0800
Update .asf.yaml
---
.asf.yaml | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/.asf.yaml b/.asf.yaml
index 957eb4d..eeb7ccf 100644
--- a/.asf.yaml
+++ b/.asf.yaml
@@ -18,6 +18,11 @@
github:
description: EventMesh go
homepage: https://eventmesh.apache.org/
+ features:
+ # Enable issue management
+ issues: true
+ # Enable wiki
+ wiki: true
labels:
- pubsub
enabled_merge_buttons:
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@eventmesh.apache.org
For additional commands, e-mail: commits-help@eventmesh.apache.org
[eventmesh-go] 06/26: Merge pull request #1 from apache/qqeasonchen-patch-1
Posted by ch...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
chenguangsheng pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/eventmesh-go.git
commit 17fc32539222bc00fd8ab84783886b1d337db7b3
Merge: 4dd0b11 0f19952
Author: mike_xwm <mi...@126.com>
AuthorDate: Wed Apr 19 14:34:09 2023 +0800
Merge pull request #1 from apache/qqeasonchen-patch-1
Update .asf.yaml
.asf.yaml | 5 +++++
1 file changed, 5 insertions(+)
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@eventmesh.apache.org
For additional commands, e-mail: commits-help@eventmesh.apache.org