Compare commits

..

6 Commits
main ... v1.0.x

Author SHA1 Message Date
Tony Chen 2f9d7f892c
http/bm: add prefix matcher for the path prefix (#2020) 3 years ago
冯国庆 a9b8af4c55
V1.0.x (#1713) 3 years ago
onlyzilla 4d2522f0b6
Compatible with Content-type: text/json when the request is in json format (#1710) 3 years ago
Vincent 9116815183
兼容 google.golang.org/grpc v1.39 版本更新 (#1156) 3 years ago
Vincent 659a56dc9d
升级 validator 到 v10 (#809) 4 years ago
Charles Ge cd831f0565
fix typo (#808) 4 years ago
  1. 2
      .github/FUNDING.yml
  2. 26
      .github/ISSUE_TEMPLATE/bug-report.md
  3. 5
      .github/ISSUE_TEMPLATE/config.yml
  4. 54
      .github/ISSUE_TEMPLATE/feature-request.md
  5. 73
      .github/ISSUE_TEMPLATE/proposal.md
  6. 10
      .github/ISSUE_TEMPLATE/question.md
  7. 87
      .github/dependabot.yml
  8. 40
      .github/pull_request_template.md
  9. 33
      .github/semantic.yml
  10. 12
      .github/stable.yml
  11. 22
      .github/workflows/codeql-analysis.yml
  12. 27
      .github/workflows/gitee-sync.yml
  13. 194
      .github/workflows/go.yml
  14. 16
      .github/workflows/issue-translator.yml
  15. 36
      .github/workflows/lint.yml
  16. 55
      .gitignore
  17. 195
      .golangci.yml
  18. 59
      .travis.yml
  19. 128
      CODE_OF_CONDUCT.md
  20. 122
      CONTRIBUTING.md
  21. 2
      LICENSE
  22. 102
      Makefile
  23. 136
      README.md
  24. 149
      README_zh.md
  25. 82
      ROADMAP.md
  26. 19
      SECURITY.md
  27. 1
      api/README.md
  28. 365
      api/metadata/metadata.pb.go
  29. 43
      api/metadata/metadata.proto
  30. 145
      api/metadata/metadata_grpc.pb.go
  31. 109
      api/metadata/metadata_http.pb.go
  32. 206
      api/metadata/server.go
  33. 207
      app.go
  34. 285
      app_test.go
  35. 15
      cmd/kratos/go.mod
  36. 79
      cmd/kratos/go.sum
  37. 28
      cmd/kratos/internal/base/install.go
  38. 24
      cmd/kratos/internal/base/install_compatible.go
  39. 62
      cmd/kratos/internal/base/mod.go
  40. 43
      cmd/kratos/internal/base/mod_test.go
  41. 108
      cmd/kratos/internal/base/path.go
  42. 126
      cmd/kratos/internal/base/repo.go
  43. 51
      cmd/kratos/internal/base/repo_test.go
  44. 58
      cmd/kratos/internal/base/vcs_url.go
  45. 55
      cmd/kratos/internal/base/vcs_url_test.go
  46. 45
      cmd/kratos/internal/change/change.go
  47. 213
      cmd/kratos/internal/change/get.go
  48. 25
      cmd/kratos/internal/change/get_test.go
  49. 67
      cmd/kratos/internal/project/add.go
  50. 64
      cmd/kratos/internal/project/new.go
  51. 149
      cmd/kratos/internal/project/project.go
  52. 29
      cmd/kratos/internal/project/project_linux_test.go
  53. 144
      cmd/kratos/internal/project/project_test.go
  54. 30
      cmd/kratos/internal/project/project_windows_test.go
  55. 78
      cmd/kratos/internal/proto/add/add.go
  56. 38
      cmd/kratos/internal/proto/add/add_test.go
  57. 40
      cmd/kratos/internal/proto/add/proto.go
  58. 52
      cmd/kratos/internal/proto/add/template.go
  59. 130
      cmd/kratos/internal/proto/client/client.go
  60. 22
      cmd/kratos/internal/proto/proto.go
  61. 120
      cmd/kratos/internal/proto/server/server.go
  62. 102
      cmd/kratos/internal/proto/server/server_test.go
  63. 142
      cmd/kratos/internal/proto/server/template.go
  64. 145
      cmd/kratos/internal/run/run.go
  65. 32
      cmd/kratos/internal/upgrade/upgrade.go
  66. 34
      cmd/kratos/main.go
  67. 4
      cmd/kratos/version.go
  68. 135
      cmd/protoc-gen-go-errors/errors.go
  69. 229
      cmd/protoc-gen-go-errors/errors/errors.pb.go
  70. 25
      cmd/protoc-gen-go-errors/errors/errors.proto
  71. 17
      cmd/protoc-gen-go-errors/errorsTemplate.tpl
  72. 62
      cmd/protoc-gen-go-errors/errors_test.go
  73. 8
      cmd/protoc-gen-go-errors/go.mod
  74. 33
      cmd/protoc-gen-go-errors/go.sum
  75. 32
      cmd/protoc-gen-go-errors/main.go
  76. 35
      cmd/protoc-gen-go-errors/template.go
  77. 4
      cmd/protoc-gen-go-errors/version.go
  78. 8
      cmd/protoc-gen-go-http/go.mod
  79. 130
      cmd/protoc-gen-go-http/go.sum
  80. 334
      cmd/protoc-gen-go-http/http.go
  81. 93
      cmd/protoc-gen-go-http/httpTemplate.tpl
  82. 87
      cmd/protoc-gen-go-http/http_test.go
  83. 35
      cmd/protoc-gen-go-http/main.go
  84. 52
      cmd/protoc-gen-go-http/template.go
  85. 4
      cmd/protoc-gen-go-http/version.go
  86. 3
      codecov.yml
  87. 25
      config/README.md
  88. 158
      config/config.go
  89. 184
      config/config_test.go
  90. 67
      config/env/env.go
  91. 429
      config/env/env_test.go
  92. 30
      config/env/watcher.go
  93. 32
      config/env/watcher_test.go
  94. 80
      config/file/file.go
  95. 341
      config/file/file_test.go
  96. 10
      config/file/format.go
  97. 43
      config/file/format_test.go
  98. 68
      config/file/watcher.go
  99. 133
      config/options.go
  100. 228
      config/options_test.go
  101. Some files were not shown because too many files have changed in this diff Show More

@ -1,2 +0,0 @@
open_collective: go-kratos
github: [go-kratos]

@ -1,26 +0,0 @@
---
name: "\U0001F41B Bug Report"
about: Report something that's broken. Please ensure your kratos version is still supported.
title: ''
labels: bug
assignees: ''
---
<!--
Please answer these questions before submitting your issue. Thanks!
For questions please use one of our forums: https://go-kratos.dev/docs/getting-started/faq
-->
#### What happened:
#### What you expected to happen:
#### How to reproduce it (as minimally and precisely as possible):
#### Anything else we need to know?:
#### Environment:
- Kratos version (use `kratos -v`):
- Go version (use `go version`):
- OS (e.g: `cat /etc/os-release`):
- Others:

@ -1,5 +0,0 @@
blank_issues_enabled: false
contact_links:
- name: Documentation issue
url: https://go-kratos.dev/en/docs/
about: For documentation issues, open a pull request at the go-kratos/go-kratos.dev repository

@ -1,54 +0,0 @@
---
name: "\U0001F4A1 Feature Request"
about: For ideas or feature requests, start a new discussion.
title: "[Feature]"
labels: feature
assignees: ''
---
Please see the FAQ in our main README.md before submitting your issue.
<!--
In order to accurately distinguish that the needs put forward by users are the needs of most users and reasonable needs, solicit community opinions through the process, and the features adopted by the community will be realized as new functions.
In order to make the proposal process as simple as possible, the process includes three stages: feature request - > proposal - > pull-request, where feature, proposal is issue and pull-request is the specific function implementation.
### Feature-request
In order to help the community correctly understand the requirements of the feature, the feature request issue needs to describe the functional requirements and relevant references or documents in detail. And the feature request issue can contain the basic description of the function, which can be used as a reference for the function implementation in the proposal.
### Proposal
Proposal contains the basic implementation methods of functions, such as interface definition, general usage of functions, etc.
### Pull-request
After the function is realized, a merge request will be initiated to associate the proposal issue with the function issue. After the merger is completed, all questions will be closed and the process will end.
### Decision process
When more than five maintainer members agree to implement the feature, a proposal issue will be created for detailed design. The status of the proposal is divided into: under discussion, finalized and abandoned. After reaching the final status, start specific implementation (PR can also be implemented synchronously during the discussion)
### Final decision maker mechanism
If the maintainer team members have major differences on a requirement, the final decision is made by @Terry Mao.
-->
### What problem is the feature used to solve?
<!--
example:
We hope to add event interface to Kratos framework to access middleware such as Kafka and rabbitmq
-->
### Requirements description of the feature
<!--
example:
The event interface should be added to Kratos. The interface should contain subscribers and publishers, and the message body should contain key value head
-->
### References
<!--
example:
- [nats](http://xxxxx)
- [kafka](http://xxxxx)
- [rabbitmq](http://xxxxx)
-->

@ -1,73 +0,0 @@
---
name: "\U0001F9F1 Proposal Request"
about: Implementation draft of feature.
title: "[Proposal]"
labels: proposal
assignees: ''
---
Please see the FAQ in our main README.md before submitting your issue.
<!--
In order to accurately distinguish that the needs put forward by users are the needs of most users and reasonable needs, solicit community opinions through the process, and the features adopted by the community will be realized as new functions.
In order to make the proposal process as simple as possible, the process includes three stages: feature request - > proposal - > pull-request, where feature, proposal is issue and pull-request is the specific function implementation.
### Feature-request
In order to help the community correctly understand the requirements of the feature, the feature request issue needs to describe the functional requirements and relevant references or documents in detail. And the feature request issue can contain the basic description of the function, which can be used as a reference for the function implementation in the proposal.
### Proposal
Proposal contains the basic implementation methods of functions, such as interface definition, general usage of functions, etc.
### Pull-request
After the function is realized, a merge request will be initiated to associate the proposal issue with the function issue. After the merger is completed, all questions will be closed and the process will end.
### Decision process
When more than five maintainer members agree to implement the feature, a proposal issue will be created for detailed design. The status of the proposal is divided into: under discussion, finalized and abandoned. After reaching the final status, start specific implementation (PR can also be implemented synchronously during the discussion)
### Final decision maker mechanism
If the maintainer team members have major differences on a requirement, the final decision is made by @Terry Mao.
-->
### Proposal description
<!--
example:
Add event interface for accessing message oriented middleware
-->
### Implementation mode
<!--
```go
example:
type Message interface {
Key() string
Value() []byte
Header() map[string]string
Ack() error
Nack() error
}
type Handler func(context.Context, Message) error
type Event interface {
Send(ctx context.Context, key string, value []byte]) error
Receive(ctx context.Context, handler Handler) error
Close() error
}
````
-->
### Usage demonstration
<!--
example:
```go
msg := kafka.NewMessage("kratos", []byte("hello world"), map[string]string{
"user": "kratos",
"phone": "123456",
})
err := sender.Send(context.Background(), msg)
```
-->

@ -1,10 +0,0 @@
---
name: "\U0001F680 Question"
about: Ask a question about Kratos.
title: "[Question]"
labels: question
assignees: ''
---
Please see the FAQ in our main README.md before submitting your issue.

@ -1,87 +0,0 @@
---
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "daily"
- package-ecosystem: "gomod"
directory: "/contrib/config/apollo"
schedule:
interval: "weekly"
- package-ecosystem: "gomod"
directory: "/contrib/config/consul"
schedule:
interval: "weekly"
- package-ecosystem: "gomod"
directory: "/contrib/config/etcd"
schedule:
interval: "weekly"
- package-ecosystem: "gomod"
directory: "/contrib/config/kubernetes"
schedule:
interval: "weekly"
- package-ecosystem: "gomod"
directory: "/contrib/config/nacos"
schedule:
interval: "weekly"
- package-ecosystem: "gomod"
directory: "/contrib/encoding/msgpack"
schedule:
interval: "weekly"
- package-ecosystem: "gomod"
directory: "/contrib/log/aliyun"
schedule:
interval: "weekly"
- package-ecosystem: "gomod"
directory: "/contrib/log/zap"
schedule:
interval: "weekly"
- package-ecosystem: "gomod"
directory: "/contrib/log/fluent"
schedule:
interval: "weekly"
- package-ecosystem: "gomod"
directory: "/contrib/metrics/datadog"
schedule:
interval: "weekly"
- package-ecosystem: "gomod"
directory: "/contrib/metrics/prometheus"
schedule:
interval: "weekly"
- package-ecosystem: "gomod"
directory: "/contrib/opensergo"
schedule:
interval: "weekly"
- package-ecosystem: "gomod"
directory: "/contrib/registry/apollo"
schedule:
interval: "weekly"
- package-ecosystem: "gomod"
directory: "/contrib/registry/consul"
schedule:
interval: "weekly"
- package-ecosystem: "gomod"
directory: "/contrib/registry/etcd"
schedule:
interval: "weekly"
- package-ecosystem: "gomod"
directory: "/contrib/registry/kubernetes"
schedule:
interval: "weekly"
- package-ecosystem: "gomod"
directory: "/contrib/registry/nacos"
schedule:
interval: "weekly"
- package-ecosystem: "gomod"
directory: "/contrib/registry/zookeeper"
schedule:
interval: "weekly"
- package-ecosystem: "gomod"
directory: "/contrib/registry/eureka"
schedule:
interval: "weekly"
- package-ecosystem: "gomod"
directory: "/contrib/registry/polaris"
schedule:
interval: "weekly"

@ -1,40 +0,0 @@
<!--
🎉 Thanks for sending a pull request to Kratos! Here are some tips for you:
1. If this is your first time contributing to Kratos, please read our contribution guide: https://go-kratos.dev/en/docs/community/contribution/
2. Ensure you have added or ran the appropriate tests and lint for your PR, please use `make lint` and `make test` before filing your PR, use `make clean` to tidy your go mod.
3. If the PR is unfinished, you may need to mark it as a WIP(Work In Progress) PR or Draft PR
4. Please use a semantic commits format title, such as `<type>[optional scope]: <description>`, see: https://go-kratos.dev/docs/community/contribution#type
5. at the same time, please note that similar work should be submitted in one PR as far as possible to reduce the workload of reviewers. Do not split a work into multiple PR unless it should.
-->
<!--
🎉 感谢您向 Kratos 发送 PR!以下是一些提示:
如果这是你第一次为 Kratos 贡献,请阅读我们的贡献指南:https://go-kratos.dev/en/docs/community/contribution/
2、确保您已经为您的 PR 添加或运行了适当的测试和lint,请在提交PR之前使用“make lint”和“make test”,使用“make clean”整理您的 go.mod。
3、如果 PR 未完成,您可能需要将其标记为 WIP(Work In Progress)PR 或 Draft PR
4、请使用语义提交格式标题,如“<类型>[可选范围]:<说明>`,请参阅:https://go-kratos.dev/docs/community/contribution#type
5. 同时请注意,同类的工作请尽量在一个PR中提交,以减轻 review 者的工作负担,不要把一项工作拆分成很多个PR,除非它应该这样做。
-->
#### Description (what this PR does / why we need it):
<!--
* The description should include the motivation for this PR or contrast this with previous behavior
-->
#### Which issue(s) this PR fixes (resolves / be part of):
<!--
* Automatically closes linked issue when PR is merged.
* If your PR is not fully resolved the issue, please use `part of #<issue number>` instead.
Usage: `fixes/resolves #<issue number>`, or `fixes/resolves (paste link of issue)`.
-->
#### Other special notes for the reviewers:
<!--
* Somethings that need extra attention for the reviewers
* Some additional notes, TODO list, etc.
-->

@ -1,33 +0,0 @@
titleOnly: true
commitOnly: false
titleAndCommits: false
scopes:
- api
- cmd
- config
- contrib
- docs
- encoding
- hack
- internal
- log
- metadata
- metrics
- middleware
- registry
- selector
- third_party
- transport
types:
- deps
- feat
- fix
- docs
- style
- refactor
- perf
- test
- build
- ci
- chore
- revert

@ -1,12 +0,0 @@
daysUntilStale: 30
daysUntilClose: 3
exemptLabels:
- pinned
- security
- bug
staleLabel: wontfix
markComment: >
This issue has been automatically marked as stale because it has not had
recent activity. It will be closed if no further activity occurs. Thank you
for your contributions.
closeComment: true

@ -1,22 +0,0 @@
name: "CodeQL"
on:
push:
branches: [ main, v1.0.x ]
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
steps:
- name: Checkout repo
uses: actions/checkout@v3
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
with:
languages: go
- name: CodeQL Analysis
uses: github/codeql-action/analyze@v2

@ -1,27 +0,0 @@
on:
push:
branches:
- main
tags:
- "*"
name: Sync to Gitee
jobs:
run:
name: Run
runs-on: ubuntu-latest
steps:
- name: Checkout source code
uses: actions/checkout@v3
- name: Mirror Github to Gitee
uses: Yikun/hub-mirror-action@v1.2
with:
src: github/go-kratos
dst: gitee/go-kratos
dst_key: ${{ secrets.GITEE_PRIVATE_KEY }}
dst_token: ${{ secrets.GITEE_TOKEN }}
account_type: org
timeout: 600
debug: true
force_update: true
static_list: "kratos"

@ -2,84 +2,130 @@ name: Go
on: on:
push: push:
branches: branches: [ master ]
- main
pull_request: pull_request:
branches: branches: [ master ]
- main
workflow_dispatch:
jobs: jobs:
build: build:
name: Build on ${{ matrix.os }} - Go${{ matrix.go_version }}
runs-on: ${{ matrix.os }}
strategy: strategy:
matrix: matrix:
go: [1.19,1.20.x] go_version:
name: build & test - 1.13
runs-on: ubuntu-latest os:
services: - ubuntu-latest
etcd:
image: gcr.io/etcd-development/etcd:v3.5.0
ports:
- 2379:2379
env:
ETCD_LISTEN_CLIENT_URLS: http://0.0.0.0:2379
ETCD_ADVERTISE_CLIENT_URLS: http://0.0.0.0:2379
consul:
image: consul:1.12.3
ports:
- 8500:8500
nacos:
image: nacos/nacos-server:v2.1.0
env:
MODE: standalone
ports:
- "8848:8848"
- "9848:9848"
polaris:
image: polarismesh/polaris-standalone:latest
ports:
- 8090:8090
- 8091:8091
- 8093:8093
steps: steps:
- uses: actions/checkout@v3
- name: Set up Go - name: Set up Go ${{ matrix.go_version }}
uses: actions/setup-go@v4.0.1 uses: actions/setup-go@v1
with: with:
go-version: ${{ matrix.go }} go-version: ${{ matrix.go_version }}
id: go
- name: Setup Environment
run: | - name: Set up Env
echo "GOPATH=$(go env GOPATH)" >> $GITHUB_ENV run: |
echo "$(go env GOPATH)/bin" >> $GITHUB_PATH echo "GOPATH=$(go env GOPATH)" >> $GITHUB_ENV
echo "$(go env GOPATH)/bin" >> $GITHUB_PATH
- name: Module cache
uses: actions/cache@v3 - name: Check out code into the Go module directory
with: uses: actions/checkout@v2
path: |
~/.cache/go-build - name: Cache dependencies
~/go/pkg/mod uses: actions/cache@v2
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} with:
restore-keys: | # Cache
${{ runner.os }}-go path: ~/go/pkg/mod
# Cache key
- name: Build key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
run: go build ./... # An ordered list of keys to use for restoring the cache if no cache hit occurred for key
restore-keys: |
- name: Test ${{ runner.os }}-go-
run: make test-coverage
- name: Get dependencies
- name: Upload coverage to Codecov run: |
run: bash <(curl -s https://codecov.io/bash) go get -v -t -d ./...
if [ -f Gopkg.toml ]; then
- name: Kratos curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh
run: | dep ensure
cd cmd/kratos fi
go build ./...
go test ./... - name: Build
run: go build ./...
- name: HTTP
run: | - name: Golangci
cd cmd/protoc-gen-go-http run: |
go build ./... curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.33.0
go test ./... golangci-lint run --out-format=github-actions
- name: Test
run: go test ./... -coverprofile=coverage.txt -covermode=atomic
- name: Coverage
run: bash <(curl -s https://codecov.io/bash)
scaffold:
name: Scaffold Test on ${{ matrix.os }} - Go${{ matrix.go_version }}
runs-on: ${{ matrix.os }}
strategy:
matrix:
go_version:
- 1.13
os:
- ubuntu-latest
steps:
- name: Set up Go ${{ matrix.go_version }}
uses: actions/setup-go@v1
with:
go-version: ${{ matrix.go_version }}
id: go
- name: Set up Env
run: |
echo "GOPATH=$(go env GOPATH)" >> $GITHUB_ENV
echo "$(go env GOPATH)/bin" >> $GITHUB_PATH
- name: Check out code into the Go module directory
uses: actions/checkout@v2
- name: Cache dependencies
uses: actions/cache@v2
with:
# Cache
path: ~/go/pkg/mod
# Cache key
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
# An ordered list of keys to use for restoring the cache if no cache hit occurred for key
restore-keys: |
${{ runner.os }}-go-
- name: Get dependencies
run: |
go get -v -t -d ./...
if [ -f Gopkg.toml ]; then
curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh
dep ensure
fi
wget https://github.com/google/protobuf/releases/download/v3.11.4/protoc-3.11.4-linux-x86_64.zip
unzip protoc-3.11.4-linux-x86_64.zip
chmod +x bin/protoc
sudo mv bin/protoc /usr/local/bin
sudo mv include /usr/local/bin
go get -u github.com/golang/protobuf/protoc-gen-go
go get -u github.com/gogo/protobuf/protoc-gen-gofast
- name: Tool
run: |
go install ./...
mkdir -p $GOPATH/src
cp -R ../kratos $GOPATH/src
cd $GOPATH/src
kratos new kratos-demo
cd kratos-demo
go build ./...

@ -1,16 +0,0 @@
name: 'issue-translator'
on:
issue_comment:
types: [created]
issues:
types: [opened]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: usthe/issues-translate-action@v2.7
with:
IS_MODIFY_TITLE: true
CUSTOM_BOT_NOTE: Bot detected the issue body's language is not English, translate it automatically. 👯👭🏻🧑🤝🧑👫🧑🏿🤝🧑🏻👩🏾🤝👨🏿👬🏿
BOT_GITHUB_TOKEN: ${{ secrets.BOT_GITHUB_TOKEN }}

@ -1,36 +0,0 @@
name: Lint
on:
push:
pull_request:
branches:
- main
workflow_dispatch:
jobs:
resolve-modules:
name: resolve module
runs-on: ubuntu-latest
outputs:
matrix: ${{ steps.set-matrix.outputs.matrix }}
steps:
- name: Checkout Repo
uses: actions/checkout@v3
- id: set-matrix
run: ./hack/resolve-modules.sh
lint:
name: lint module
runs-on: ubuntu-latest
needs: resolve-modules
strategy:
matrix: ${{ fromJson(needs.resolve-modules.outputs.matrix) }}
steps:
- uses: actions/checkout@v3
- name: Lint
uses: golangci/golangci-lint-action@v3
with:
version: latest
working-directory: ${{ matrix.workdir }}
skip-pkg-cache: true

55
.gitignore vendored

@ -1,39 +1,32 @@
# Reference https://github.com/github/gitignore/blob/master/Go.gitignore # idea ignore
# Binaries for programs and plugins .idea/
*.ipr
*.iml
*.iws
.vscode/
# temp ignore
*.log
*.cache
*.diff
*.exe *.exe
*.exe~ *.exe~
*.dll *.patch
*.dylib *.swp
*.tmp
# Test binary, built with `go test -c`
*.test
# Output of the go coverage tool, specifically when used with LiteIDE
*.out
# Dependency directories (remove the comment below to include it)
vendor/
# Go workspace file
go.work
go.work.sum
# Compiled Object files, Static and Dynamic libs (Shared Objects)
*.o
*.a
*.so
# OS General # system ignore
Thumbs.db
.DS_Store .DS_Store
Thumbs.db
# project # project
*.cert *.cert
*.key *.key
*.log tool/kratos/kratos
bin/ tool/kratos-protoc/kratos-protoc
tool/kratos-gen-bts/kratos-gen-bts
# Develop tools tool/kratos-gen-mc/kratos-gen-mc
.vscode/ tool/kratos/kratos-protoc/kratos-protoc
.idea/ tool/kratos/protobuf/protoc-gen-bm/protoc-gen-bm
*.swp tool/kratos/protobuf/protoc-gen-ecode/protoc-gen-ecode
tool/kratos/protobuf/protoc-gen-bswagger/protoc-gen-bswagger

@ -1,71 +1,138 @@
# [index] https://github.com/golangci/golangci-lint
# [example] https://github.com/golangci/golangci-lint/blob/master/.golangci.example.yml
run: run:
timeout: 5m tests: true #是否包含测试文件
modules-download-mode: readonly issues-exit-code: 0
linters-settings:
# govet:
# check-shadowing: true #启用了对同名变量名在函数中被隐藏的警告
gofmt:
simplify: true
goimports:
local-prefixes: "github.com/go-kratos/kratos" # 格式化代码时,本地代码单独块
gocritic:
enabled-tags:
- diagnostic
# - style
# - performance
disabled-checks:
#- wrapperFunc
#- dupImport # https://github.com/go-critic/go-critic/issues/845
- commentedOutCode
- ifElseChain
- elseif
settings: # settings passed to gocritic
captLocal: # must be valid enabled check name
paramsOnly: true
# rangeValCopy:
# sizeThreshold: 32
lll:
line-length: 500
funlen:
lines: 500
statements: 500
gocyclo:
min-complexity: 100
linters: linters:
disable-all: true disable-all: true
fast: false
enable: enable:
- bodyclose # https://golangci-lint.run/usage/configuration/
- deadcode - bodyclose # http.resp.body 内存泄露检查
- dogsled - deadcode # 无用的变量声明检查
- durationcheck - depguard # 自定义依赖包白、黑名单 控制导包
- errcheck - dogsled # 空白标识符的赋值检查 默认为2
- exportloopref #- dupl # 重复代码检查
- govet - errcheck # 未判断的error返回值检查
- gosimple - funlen # 接口最大行数检查
- gofmt #- gochecknoinits # 包中定义init()函数检查
- gofumpt #- goconst # 常量字符串检查
- goconst - gocritic #
- goimports - gocyclo # 代码复杂度检查
- gomnd - gofmt # 优化代码
- gocyclo - goimports # 自动增加和删除包
- ineffassign - golint # 代码风格检查
- lll #- gomnd # 参数、赋值、用例、条件、操作和返回语句检查
- prealloc - goprintffuncname #
- revive - gosec # 源代码安全检查
- staticcheck - gosimple # 可以优化的代码检查 注:该工具已整合到staticcheck中
- structcheck - govet # 代码正确性检查
- typecheck - ineffassign # 无效赋值检查
- unused - interfacer # 建议接口的使用方式
- varcheck - lll # 行最大字符
- whitespace - misspell # 拼写错误检查
- wastedassign - nakedret # 大于指定函数长度的函数的无约束返回值检查
- unconvert - nolintlint #
- rowserrcheck # sql.Rows.Err检查
- scopelint # 循环变量引用检查,排除test文件
- staticcheck # 静态检查
- structcheck # 结构体字段的约束条件检查
- stylecheck # 代码风格检查
- typecheck # 类型检查
- unconvert # 类型转换检查
- unparam # 未使用参数检查
#- unused # 未使用变量、函数检查
- varcheck # 报告exported变量和常量
- whitespace # 空行检查
# don't enable: severity:
# - asciicheck # Default value is empty string.
# - scopelint # Set the default severity for issues. If severity rules are defined and the issues
# - gochecknoglobals # do not match or no severity is provided to the rule this will be the default
# - gocognit # severity applied. Severities should match the supported severity names of the
# - godot # selected out format.
# - godox # - Code climate: https://docs.codeclimate.com/docs/issues#issue-severity
# - goerr113 # - Checkstyle: https://checkstyle.sourceforge.io/property_types.html#severity
# - interfacer # - Github: https://help.github.com/en/actions/reference/workflow-commands-for-github-actions#setting-an-error-message
# - maligned default-severity: error
# - nestif # The default value is false.
# - prealloc # If set to true severity-rules regular expressions become case sensitive.
# - testpackage case-sensitive: false
# - stylrcheck # Default value is empty list.
# - wsl # When a list of severity rules are provided, severity information will be added to lint
# issues. Severity rules have the same filtering capability as exclude rules except you
# are allowed to specify one matcher per severity rule.
# Only affects out formats that support setting severity information.
rules:
- linters:
- dupl
- nakedret
- lll
- misspell
- goprintffuncname
- stylecheck
- deadcode
- whitespace
- unparam
- golint
- gosec
- staticcheck
- structcheck
- gocritic
- errcheck
- rowserrcheck
- unconvert
- gosimple
- rowserrcheck
- ineffassign
severity: warning
linters-settings: issues:
govet: # Excluding configuration per-path, per-linter, per-text and per-source
check-shadowing: true exclude-rules:
whitespace: - path: _test\.go
multi-func: true linters:
lll: - gomnd
line-length: 160 - gocyclo
gomnd: - errcheck
# don't include the "operation", "argument" and "assign" - dupl
checks: - gosec
- case - scopelint
- condition - interfacer
- return - govet
goconst: # https://github.com/go-critic/go-critic/issues/926
ignore-tests: true - linters:
gocyclo: - gocritic
# recommend 10-20 text: "unnecessaryDefer:"
min-complexity: 50
goimports:
local-prefixes: github.com/go-kratos # Put imports beginning with prefix after 3rd-party packages

@ -0,0 +1,59 @@
language: go
go:
- 1.12.x
- 1.13.x
services:
- docker
# Only clone the most recent commit.
git:
depth: 1
# Force-enable Go modules. This will be unnecessary when Go 1.12 lands.
env:
global:
- GO111MODULE=on
- REGION=sh
- ZONE=sh001
- DEPLOY_ENV=dev
- DISCOVERY_NODES=127.0.0.1:7171
- HTTP_PERF=tcp://0.0.0.0:0
- DOCKER_COMPOSE_VERSION=1.24.1
- ZK_VERSION=3.5.6
before_install:
# docker-compose
- sudo rm /usr/local/bin/docker-compose
- curl -L https://github.com/docker/compose/releases/download/${DOCKER_COMPOSE_VERSION}/docker-compose-`uname -s`-`uname -m` > docker-compose
- chmod +x docker-compose
- sudo mv docker-compose /usr/local/bin
# zookeeper
- wget "http://apache.cs.utah.edu/zookeeper/zookeeper-${ZK_VERSION}/apache-zookeeper-${ZK_VERSION}-bin.tar.gz"
- tar -xvf "apache-zookeeper-${ZK_VERSION}-bin.tar.gz"
- mv apache-zookeeper-${ZK_VERSION}-bin zk
- chmod +x ./zk/bin/zkServer.sh
# Skip the install step. Don't `go get` dependencies. Only build with the code
# in vendor/
install: true
# Anything in before_script that returns a nonzero exit code will flunk the
# build and immediately stop. It's sorta like having set -e enabled in bash.
# Make sure golangci-lint is vendored.
before_script:
- curl -sfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh| sh -s -- -b $GOPATH/bin
# discovery
- curl -sfL https://raw.githubusercontent.com/bilibili/discovery/master/install.sh | sh -s -- -b $GOPATH/bin
- curl -sfL https://raw.githubusercontent.com/bilibili/discovery/master/cmd/discovery/discovery-example.toml -o $GOPATH/bin/discovery.toml
- nohup bash -c "$GOPATH/bin/discovery -conf $GOPATH/bin/discovery.toml &"
# zookeeper
- sudo ./zk/bin/zkServer.sh start ./zk/conf/zoo_sample.cfg 1> /dev/null
script:
- go build ./...
- go test ./...
after_success:
- golangci-lint run # run a bunch of code checkers/linters in parallel

@ -1,128 +0,0 @@
# Contributor Covenant Code of Conduct
## Our Pledge
We as members, contributors, and leaders pledge to make participation in our
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status,
nationality, personal appearance, race, religion, or sexual identity
and orientation.
We pledge to act and interact in ways that contribute to an open, welcoming,
diverse, inclusive, and healthy community.
## Our Standards
Examples of behavior that contributes to a positive environment for our
community include:
* Demonstrating empathy and kindness toward other people
* Being respectful of differing opinions, viewpoints, and experiences
* Giving and gracefully accepting constructive feedback
* Accepting responsibility and apologizing to those affected by our mistakes,
and learning from the experience
* Focusing on what is best not just for us as individuals, but for the
overall community
Examples of unacceptable behavior include:
* The use of sexualized language or imagery, and sexual attention or
advances of any kind
* Trolling, insulting or derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or email
address, without their explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Enforcement Responsibilities
Community leaders are responsible for clarifying and enforcing our standards of
acceptable behavior and will take appropriate and fair corrective action in
response to any behavior that they deem inappropriate, threatening, offensive,
or harmful.
Community leaders have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions that are
not aligned to this Code of Conduct, and will communicate reasons for moderation
decisions when appropriate.
## Scope
This Code of Conduct applies within all community spaces, and also applies when
an individual is officially representing the community in public spaces.
Examples of representing our community include using an official e-mail address,
posting via an official social media account, or acting as an appointed
representative at an online or offline event.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at
iammao@vip.qq.com.
All complaints will be reviewed and investigated promptly and fairly.
All community leaders are obligated to respect the privacy and security of the
reporter of any incident.
## Enforcement Guidelines
Community leaders will follow these Community Impact Guidelines in determining
the consequences for any action they deem in violation of this Code of Conduct:
### 1. Correction
**Community Impact**: Use of inappropriate language or other behavior deemed
unprofessional or unwelcome in the community.
**Consequence**: A private, written warning from community leaders, providing
clarity around the nature of the violation and an explanation of why the
behavior was inappropriate. A public apology may be requested.
### 2. Warning
**Community Impact**: A violation through a single incident or series
of actions.
**Consequence**: A warning with consequences for continued behavior. No
interaction with the people involved, including unsolicited interaction with
those enforcing the Code of Conduct, for a specified period of time. This
includes avoiding interactions in community spaces as well as external channels
like social media. Violating these terms may lead to a temporary or
permanent ban.
### 3. Temporary Ban
**Community Impact**: A serious violation of community standards, including
sustained inappropriate behavior.
**Consequence**: A temporary ban from any sort of interaction or public
communication with the community for a specified period of time. No public or
private interaction with the people involved, including unsolicited interaction
with those enforcing the Code of Conduct, is allowed during this period.
Violating these terms may lead to a permanent ban.
### 4. Permanent Ban
**Community Impact**: Demonstrating a pattern of violation of community
standards, including sustained inappropriate behavior, harassment of an
individual, or aggression toward or disparagement of classes of individuals.
**Consequence**: A permanent ban from any sort of public interaction within
the community.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 2.0, available at
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
Community Impact Guidelines were inspired by [Mozilla's code of conduct
enforcement ladder](https://github.com/mozilla/diversity).
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see the FAQ at
https://www.contributor-covenant.org/faq. Translations are available at
https://www.contributor-covenant.org/translations.

@ -1,122 +0,0 @@
The kratos community wants to be helped by a wide range of developers, so you'd like to take a few minutes to read this guide before you mention the problem or pull request.
## Reporting Bug or Fixing Bugs
We use GitHub issues to manage issues. If you want to submit , first make sure you've searched for existing issues, pull requests and read our [FAQ](https://go-kratos.dev/docs/intro/faq).
When submitting a bug report, use the issue template we provide to clearly describe the problems encountered and how to reproduce, and if convenient it is best to provide a minimal reproduce repository.
## Adding new features
In order to accurately distinguish whether the needs put forward by users are the needs or reasonable needs of most users, solicit opinions from the community through the proposal process, and the proposals adopted by the community will be realized as new feature.
In order to make the proposal process as simple as possible, the process includes three stages: proposal, feature and PR, in which proposal, feature is issue and PR is the specific function implementation.
In order to facilitate the community to correctly understand the requirements of the proposal, the proposal issue needs to describe the functional requirements and relevant references or literature in detail.
When most community users agree with this proposal, they will create a feature issue associated with the proposal issue.
The feature issue needs to describe the implementation method and function demonstration in detail as a reference for the final function implementation.
After the function is implemented, a merge request will be initiated to associate the proposal issue and feature issue.
After the merge is completed, Close all issues.
## How to submit code
If you've never submitted code on GitHub, follow these steps:
- First, please fork items to your GitHub account
- Then create a new feature branch based on the main branch and name it features such as feature-log
- Write code
- Submit code to the far end branch
- Submit a PR request in github
- Wait for review and merge to the main branch
**Note That when you submit a PR request, you first ensure that the code uses the correct coding specifications and that there are complete test cases, and that the information in the submission of the PR is best associated with the relevant issue to ease the workload of the auditor.**
## Conventional Commits
```
<type>[optional scope]: <description>
[optional body]
[optional footer(s)]
```
> More: [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/#summary)
### type
There are the following types of commit:
#### Main
- **fix**: A bug fix
- **feat**: A new feature
- **deps**: Changes external dependencies
- **break**: Changes has break change
#### Other
- **docs**: Documentation only changes
- **refactor**: A code change that neither fixes a bug nor adds a feature
- **style**: Changes that do not affect the meaning of the code (white-space, formatting, etc)
- **test**: Adding missing tests or correcting existing tests
- **chore** Daily work, examples, etc.
- **ci**: Changes to our CI configuration files and scripts
### scope
The following is the list of supported scopes:
- transport
- examples
- middleware
- config
- cmd
- etc.
### description
The description contains a succinct description of the change
- use the imperative, present tense: "change" not "changed" nor "changes"
- don't capitalize the first letter
- no dot (.) at the end
### body
The body should include the motivation for the change and contrast this with previous behavior.
### footer
The footer should contain any information about **Breaking Changes** and is also the place to reference GitHub issues that this commit Closes.
### examples
#### Only commit message
```
fix: The log debug level should be -1
```
#### Attention
```
refactor!(transport/http): replacement underlying implementation
```
#### Full commit message
```
fix(log): [BREAKING-CHANGE] unable to meet the requirement of log Library
Explain the reason, purpose, realization method, etc.
Close #777
Doc change on doc/#111
BREAKING CHANGE:
Breaks log.info api, log.log should be used instead
```
## Release
You can use `kratos changelog dev` to generate a change log during.
The following is the list of supported types:
- Breaking Change
- Dependencies
- Bug Fixes
- Others

@ -1,6 +1,6 @@
MIT License MIT License
Copyright (c) 2020 go-kratos Copyright (c) 2018 bilibili
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal

@ -1,102 +0,0 @@
user := $(shell whoami)
rev := $(shell git rev-parse --short HEAD)
os := $(shell expr substr $(shell uname -s) 1 5)
# GOBIN > GOPATH > INSTALLDIR
# Mac OS X
ifeq ($(shell uname),Darwin)
GOBIN := $(shell echo ${GOBIN} | cut -d':' -f1)
GOPATH := $(shell echo $(GOPATH) | cut -d':' -f1)
endif
# Linux
ifeq ($(os),Linux)
GOBIN := $(shell echo ${GOBIN} | cut -d':' -f1)
GOPATH := $(shell echo $(GOPATH) | cut -d':' -f1)
endif
# Windows
ifeq ($(os),MINGW)
GOBIN := $(subst \,/,$(GOBIN))
GOPATH := $(subst \,/,$(GOPATH))
GOBIN :=/$(shell echo "$(GOBIN)" | cut -d';' -f1 | sed 's/://g')
GOPATH :=/$(shell echo "$(GOPATH)" | cut -d';' -f1 | sed 's/://g')
endif
BIN := ""
TOOLS_SHELL="./hack/tools.sh"
# golangci-lint
LINTER := bin/golangci-lint
# check GOBIN
ifneq ($(GOBIN),)
BIN=$(GOBIN)
else
# check GOPATH
ifneq ($(GOPATH),)
BIN=$(GOPATH)/bin
endif
endif
$(LINTER):
curl -SL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s latest
all:
@cd cmd/kratos && go build && cd - &> /dev/null
@cd cmd/protoc-gen-go-errors && go build && cd - &> /dev/null
@cd cmd/protoc-gen-go-http && go build && cd - &> /dev/null
.PHONY: install
install: all
ifeq ($(user),root)
#root, install for all user
@cp ./cmd/kratos/kratos /usr/bin
@cp ./cmd/protoc-gen-go-errors/protoc-gen-go-errors /usr/bin
@cp ./cmd/protoc-gen-go-http/protoc-gen-go-http /usr/bin
else
#!root, install for current user
$(shell if [ -z '$(BIN)' ]; then read -p "Please select installdir: " REPLY; mkdir -p $${REPLY};\
cp ./cmd/kratos/kratos $${REPLY}/;cp ./cmd/protoc-gen-go-errors/protoc-gen-go-errors $${REPLY}/;cp ./cmd/protoc-gen-go-http/protoc-gen-go-http $${REPLY}/;else mkdir -p '$(BIN)';\
cp ./cmd/kratos/kratos '$(BIN)';cp ./cmd/protoc-gen-go-errors/protoc-gen-go-errors '$(BIN)';cp ./cmd/protoc-gen-go-http/protoc-gen-go-http '$(BIN)'; fi)
endif
@which protoc-gen-go &> /dev/null || go get google.golang.org/protobuf/cmd/protoc-gen-go
@which protoc-gen-go-grpc &> /dev/null || go get google.golang.org/grpc/cmd/protoc-gen-go-grpc
@which protoc-gen-validate &> /dev/null || go get github.com/envoyproxy/protoc-gen-validate
@echo "install finished"
.PHONY: uninstall
uninstall:
$(shell for i in `which -a kratos | grep -v '/usr/bin/kratos' 2>/dev/null | sort | uniq`; do read -p "Press to remove $${i} (y/n): " REPLY; if [ $${REPLY} = "y" ]; then rm -f $${i}; fi; done)
$(shell for i in `which -a protoc-gen-go-grpc | grep -v '/usr/bin/protoc-gen-go-errors' 2>/dev/null | sort | uniq`; do read -p "Press to remove $${i} (y/n): " REPLY; if [ $${REPLY} = "y" ]; then rm -f $${i}; fi; done)
$(shell for i in `which -a protoc-gen-validate | grep -v '/usr/bin/protoc-gen-go-errors' 2>/dev/null | sort | uniq`; do read -p "Press to remove $${i} (y/n): " REPLY; if [ $${REPLY} = "y" ]; then rm -f $${i}; fi; done)
@echo "uninstall finished"
.PHONY: clean
clean:
@${TOOLS_SHELL} tidy
@echo "clean finished"
.PHONY: fix
fix: $(LINTER)
@${TOOLS_SHELL} fix
@echo "lint fix finished"
.PHONY: test
test:
@${TOOLS_SHELL} test
@echo "go test finished"
.PHONY: test-coverage
test-coverage:
@${TOOLS_SHELL} test_coverage
@echo "go test with coverage finished"
.PHONY: lint
lint: $(LINTER)
@${TOOLS_SHELL} lint
@echo "lint check finished"
.PHONY: proto
proto:
protoc --proto_path=./api --proto_path=./third_party --go_out=paths=source_relative:./api --go-grpc_out=paths=source_relative:./api --go-http_out=paths=source_relative:./api metadata/metadata.proto
protoc --proto_path=./third_party --go_out=paths=source_relative:./errors/errors.proto

@ -1,114 +1,76 @@
<p align="center"><a href="https://go-kratos.dev/" target="_blank"><img src="https://github.com/go-kratos/kratos/blob/main/docs/images/kratos-large.png?raw=true"></a></p> ![kratos](docs/img/kratos3.png)
<p align="center"> [![Language](https://img.shields.io/badge/Language-Go-blue.svg)](https://golang.org/)
<a href="https://github.com/go-kratos/kratos/actions"><img src="https://github.com/go-kratos/kratos/workflows/Go/badge.svg" alt="Build Status"></a> [![Build Status](https://github.com/go-kratos/kratos/workflows/Go/badge.svg)](https://github.com/go-kratos/kratos/actions)
<a href="https://pkg.go.dev/github.com/go-kratos/kratos/v2"><img src="https://pkg.go.dev/badge/github.com/go-kratos/kratos/v2" alt="GoDoc"></a> [![GoDoc](https://godoc.org/github.com/go-kratos/kratos?status.svg)](https://godoc.org/github.com/go-kratos/kratos)
<a href="https://codecov.io/gh/go-kratos/kratos"><img src="https://codecov.io/gh/go-kratos/kratos/master/graph/badge.svg" alt="codeCov"></a> [![Go Report Card](https://goreportcard.com/badge/github.com/go-kratos/kratos)](https://goreportcard.com/report/github.com/go-kratos/kratos)
<a href="https://goreportcard.com/report/github.com/go-kratos/kratos"><img src="https://goreportcard.com/badge/github.com/go-kratos/kratos" alt="Go Report Card"></a> [![Discord](https://img.shields.io/discord/766619759214854164?label=chat&logo=discord)](https://discord.gg/BWzJsUJ)
<a href="https://github.com/go-kratos/kratos/blob/main/LICENSE"><img src="https://img.shields.io/github/license/go-kratos/kratos" alt="License"></a>
<a href="https://github.com/avelino/awesome-go"><img src="https://awesome.re/mentioned-badge.svg" alt="Awesome Go"></a>
<a href="https://discord.gg/BWzJsUJ"><img src="https://img.shields.io/discord/766619759214854164?label=chat&logo=discord" alt="Discord"></a>
</p>
<p align="center">
<a href="https://www.producthunt.com/posts/go-kratos?utm_source=badge-featured&utm_medium=badge&utm_souce=badge-go-kratos" target="_blank"><img src="https://api.producthunt.com/widgets/embed-image/v1/featured.svg?post_id=306565&theme=light" alt="Go Kratos - A Go framework for microservices. | Product Hunt" style="width: 250px; height: 54px;" width="250" height="54" /></a>
</p>
##### Translate to: [简体中文](README_zh.md) # Kratos
## About Kratos Kratos是[bilibili](https://www.bilibili.com)开源的一套Go微服务框架,包含大量微服务相关框架及工具。
> The name is inspired by the Greek-mythology-based game "God of War". It tells the adventures of Kratos becoming a god of war from a mortal and launching a god-killing slaughter. > 名字来源于:《战神》游戏以希腊神话为背景,讲述由凡人成为战神的奎托斯(Kratos)成为战神并展开弑神屠杀的冒险历程。
Kratos is a microservice-oriented governance framework implemented by golang, which offers convenient capabilities to help you quickly build a bulletproof application from scratch, such as: ## Goals
- The [communication protocol](https://go-kratos.dev/en/docs/component/api) is based on the HTTP/gRPC through the definition of Protobuf. 我们致力于提供完整的微服务研发体验,整合相关框架及工具后,微服务治理相关部分可对整体业务开发周期无感,从而更加聚焦于业务交付。对每位开发者而言,整套Kratos框架也是不错的学习仓库,可以了解和参考到[bilibili](https://www.bilibili.com)在微服务方面的技术积累和经验。
- Abstract [transport](https://go-kratos.dev/en/docs/component/transport/overview) layer support: [HTTP](https://go-kratos.dev/en/docs/component/transport/http) / [gRPC](https://go-kratos.dev/en/docs/component/transport/grpc).
- Powerful [middleware](https://go-kratos.dev/en/docs/component/middleware/overview) design, support: [Tracing (OpenTelemetry)](https://go-kratos.dev/en/docs/component/middleware/tracing), [Metrics (Prometheus is default)](https://go-kratos.dev/en/docs/component/middleware/metrics), [Recovery](https://go-kratos.dev/en/docs/component/middleware/recovery) and more.
- [Registry](https://go-kratos.dev/en/docs/component/registry) interface able to be connected with various other centralized registries through plug-ins.
- The [standard log interfaces](https://go-kratos.dev/en/docs/component/log) ease the integration of the third-party log libs with logs collected through the *Fluentd*.
- Automatically support the selection of the content [encoding](https://go-kratos.dev/en/docs/component/encoding) with Accept and Content-Type.
- Multiple data sources are supported for [configurations](https://go-kratos.dev/en/docs/component/config) and dynamic configurations (use atomic operations).
- In the protocol of HTTP/gRPC, use the uniform [metadata](https://go-kratos.dev/en/docs/component/metadata) transfer method.
- You can define [errors](https://go-kratos.dev/en/docs/component/errors/) in protos and generate enums with protoc-gen-go.
- You can define [verification rules](https://go-kratos.dev/en/docs/component/middleware/validate) in Protobuf supported by the HTTP/gRPC service.
- [Swagger API](https://go-kratos.dev/en/docs/guide/openapi) is generated Automatically and embed Swagger UI endpoint can be started by adding [Swagger plugin](https://github.com/go-kratos/swagger-api).
Kratos is accessible, powerful, and provides tools required for large, robust applications. ## Features
* HTTP Blademaster:核心基于[gin](https://github.com/gin-gonic/gin)进行模块化设计,简单易用、核心足够轻量;
* GRPC Warden:基于官方gRPC开发,集成[discovery](https://github.com/bilibili/discovery)服务发现,并融合P2C负载均衡;
* Cache:优雅的接口化设计,非常方便的缓存序列化,推荐结合代理模式[overlord](https://github.com/bilibili/overlord);
* Database:集成MySQL/HBase/TiDB,添加熔断保护和统计支持,可快速发现数据层压力;
* Config:方便易用的[paladin sdk](https://go-kratos.github.io/kratos/#/config),可配合远程配置中心,实现配置版本管理和更新;
* Log:类似[zap](https://github.com/uber-go/zap)的field实现高性能日志库,并结合log-agent实现远程日志管理;
* Trace:基于opentracing,集成了全链路trace支持(gRPC/HTTP/MySQL/Redis/Memcached);
* Kratos Tool:工具链,可快速生成标准项目,或者通过Protobuf生成代码,非常便捷使用gRPC、HTTP、swagger文档;
## Learning Kratos ## Quick start
Kratos has the most extensive and thorough [documentation](https://go-kratos.dev/en/docs/getting-started/start) and [example](https://github.com/go-kratos/examples) library of all modern web application frameworks, making it a breeze to get started with the framework. ### Requirments
We also provide a [modern template](https://github.com/go-kratos/kratos-layout). This template should help reduce the work required to setup up modern projects. Go version>=1.13
### Goals ### Installation
```shell
Kratos boosts your productivity. With the integration of excellent resources and further support, programmers can get rid of most issues might encounter in the field of distributed systems and software engineering such that they are allowed to focus on the release of businesses only. Additionally, for each programmer, Kratos is also an ideal one learning warehouse for many aspects of microservices to enrich their experiences and skills. # Linux/macOS
GO111MODULE=on && go get -u github.com/go-kratos/kratos/tool/kratos
### Principles
* **Simple**: Appropriate design with plain and easy code.
* **General**: Cover the various utilities for business development.
* **Highly efficient**: Speeding up the efficiency of businesses upgrading.
* **Stable**: The base libs validated in the production environment have the characteristics of high testability, high coverage as well as high security and reliability.
* **Robust**: Eliminating misusing through high quality of the base libs.
* **High-performance**: Optimal performance excluding the optimization of hacking in case of *unsafe*. 
* **Expandability**: Properly designed interfaces where you can expand utilities such as base libs to meet your further requirements.
* **Fault-tolerance**: Designed against failure, enhance the understanding and exercising of SRE within Kratos to achieve more robustness.
* **Toolchain**: Includes an extensive toolchain, such as the code generation of cache, the lint tool, and so forth.
## Getting Started # Windows (Powershell)
go env -w GO111MODULE=on ; go get -u github.com/go-kratos/kratos/tool/kratos
Create a kratos playground through [docker](https://www.docker.com/products/docker-desktop): # Windows (CMD)
go env -w GO111MODULE=on && go get -u github.com/go-kratos/kratos/tool/kratos
```shell cd $GOPATH/src
docker run -it --rm -p 8000:8000 --workdir /workspace golang kratos new kratos-demo
``` ```
```shell 通过 `kratos new` 会快速生成基于kratos库的脚手架代码,如生成 [kratos-demo](https://github.com/bilibili/kratos-demo)
apt-get update && apt-get -y install protobuf-compiler
export GOPROXY=https://goproxy.io,direct ### Build & Run
go install github.com/go-kratos/kratos/cmd/kratos/v2@latest && kratos upgrade
```
```shell ```shell
kratos new helloworld cd kratos-demo/cmd
cd helloworld/ && go mod tidy go build
kratos run ./cmd -conf ../configs
``` ```
Use a browser to open and visit: `http://localhost:8000/helloworld/kratos`, The kratos program is running! 打开浏览器访问:[http://localhost:8000/kratos-demo/start](http://localhost:8000/kratos-demo/start),你会看到输出了`Golang 大法好 !!!`
If you need more, please visit the kratos [documentation](https://go-kratos.dev/en/docs/getting-started/start). [快速开始](https://go-kratos.github.io/kratos/#/quickstart) [kratos工具](https://go-kratos.github.io/kratos/#/kratos-tool)
## Security Vulnerabilities ## Documentation
If you discover a security vulnerability within Kratos, please send an e-mail to tonybase via go-kratos@googlegroups.com. All security vulnerabilities will be promptly addressed. > [简体中文](https://go-kratos.github.io/kratos)
> [简体中文(国内镜像)](https://go-kratos.gitee.io/kratos/)
> [FAQ](https://go-kratos.github.io/kratos/#/FAQ)
## Community ## 社区
* [官方微信群](https://github.com/go-kratos/kratos/issues/682) (推荐)
- [Wechat Group](https://github.com/go-kratos/kratos/issues/682) * [Discord Group](https://discord.gg/BWzJsUJ)
- [Discord Group](https://discord.gg/BWzJsUJ)
- [go-kratos.dev](https://go-kratos.dev/en)
## Contributors
Thank you for considering contributing to the Kratos framework! The contribution guide can be found in the [Kratos documentation](https://go-kratos.dev/en/docs/community/contribution).
<a href="https://github.com/go-kratos/kratos/graphs/contributors">
<img src="https://contrib.rocks/image?repo=go-kratos/kratos" />
</a>
## License ## License
Kratos is under the MIT license. See the [LICENSE](./LICENSE) file for details.
The Kratos framework is open-sourced software licensed under the [MIT license](./LICENSE).
## Acknowledgments
The following project had particular influence on kratos's design.
- [go-kit/kit](https://github.com/go-kit/kit) is a programming toolkit for building microservices in go.
- [asim/go-micro](https://github.com/asim/go-micro) a distributed systems development framework.
- [google/go-cloud](https://github.com/google/go-cloud) is go cloud development kit.
- [zeromicro/go-zero](https://github.com/zeromicro/go-zero) is a web and rpc framework with lots of builtin engineering practices.
- [beego/beego](https://github.com/beego/beego) is a web framework including RESTful APIs, web apps and backend services.

@ -1,149 +0,0 @@
<p align="center"><a href="https://go-kratos.dev/" target="_blank"><img src="https://github.com/go-kratos/kratos/blob/main/docs/images/kratos-large.png?raw=true"></a></p>
<p align="center">
<a href="https://github.com/go-kratos/kratos/actions"><img src="https://github.com/go-kratos/kratos/workflows/Go/badge.svg" alt="Build Status"></a>
<a href="https://pkg.go.dev/github.com/go-kratos/kratos/v2"><img src="https://pkg.go.dev/badge/github.com/go-kratos/kratos/v2" alt="GoDoc"></a>
<a href="https://codecov.io/gh/go-kratos/kratos"><img src="https://codecov.io/gh/go-kratos/kratos/master/graph/badge.svg" alt="codeCov"></a>
<a href="https://goreportcard.com/report/github.com/go-kratos/kratos"><img src="https://goreportcard.com/badge/github.com/go-kratos/kratos" alt="Go Report Card"></a>
<a href="https://github.com/go-kratos/kratos/blob/main/LICENSE"><img src="https://img.shields.io/github/license/go-kratos/kratos" alt="License"></a>
<a href="https://github.com/avelino/awesome-go"><img src="https://awesome.re/mentioned-badge.svg" alt="Awesome Go"></a>
<a href="https://discord.gg/BWzJsUJ"><img src="https://img.shields.io/discord/766619759214854164?label=chat&logo=discord" alt="Discord"></a>
</p>
<p align="center">
<a href="https://www.producthunt.com/posts/go-kratos?utm_source=badge-featured&utm_medium=badge&utm_souce=badge-go-kratos" target="_blank"><img src="https://api.producthunt.com/widgets/embed-image/v1/featured.svg?post_id=306565&theme=light" alt="Go Kratos - A Go framework for microservices. | Product Hunt" style="width: 250px; height: 54px;" width="250" height="54" /></a>
</p>
Translations: [English](README.md) | [简体中文](README_zh.md)
# Kratos
Kratos 一套轻量级 Go 微服务框架,包含大量微服务相关功能及工具。
> 名字来源于:《战神》游戏以希腊神话为背景,讲述奎托斯(Kratos)由凡人成为战神并展开弑神屠杀的冒险经历。
## Goals
我们致力于提供完整的微服务研发体验,整合相关框架及工具后,微服务治理相关部分可对整体业务开发周期无感,从而更加聚焦于业务交付。对每位开发者而言,整套 Kratos 框架也是不错的学习仓库,可以了解和参考到微服务方面的技术积累和经验。
### Principles
* 简单:不过度设计,代码平实简单;
* 通用:通用业务开发所需要的基础库的功能;
* 高效:提高业务迭代的效率;
* 稳定:基础库可测试性高,覆盖率高,有线上实践安全可靠;
* 健壮:通过良好的基础库设计,减少错用;
* 高性能:性能高,但不特定为了性能做 hack 优化,引入 unsafe ;
* 扩展性:良好的接口设计,来扩展实现,或者通过新增基础库目录来扩展功能;
* 容错性:为失败设计,大量引入对 SRE 的理解,鲁棒性高;
* 工具链:包含大量工具链,比如 cache 代码生成,lint 工具等等;
## Features
* [APIs](https://go-kratos.dev/docs/component/api) :协议通信以 HTTP/gRPC 为基础,通过 Protobuf 进行定义;
* [Errors](https://go-kratos.dev/docs/component/errors/) :通过 Protobuf 的 Enum 作为错误码定义,以及工具生成判定接口;
* [Metadata](https://go-kratos.dev/docs/component/metadata) :在协议通信 HTTP/gRPC 中,通过 Middleware 规范化服务元信息传递;
* [Config](https://go-kratos.dev/docs/component/config) :支持多数据源方式,进行配置合并铺平,通过 Atomic 方式支持动态配置;
* [Logger](https://go-kratos.dev/docs/component/log) :标准日志接口,可方便集成三方 log 库,并可通过 fluentd 收集日志;
* [Metrics](https://go-kratos.dev/docs/component/middleware/metrics) :统一指标接口,可以实现各种指标系统,默认集成 Prometheus;
* [Tracing](https://go-kratos.dev/docs/component/middleware/tracing) :遵循 OpenTelemetry 规范定义,以实现微服务链路追踪;
* [Encoding](https://go-kratos.dev/docs/component/encoding) :支持 Accept 和 Content-Type 进行自动选择内容编码;
* [Transport](https://go-kratos.dev/docs/component/transport/overview) :通用的 [HTTP](https://go-kratos.dev/docs/component/transport/http) /[gRPC](https://go-kratos.dev/docs/component/transport/grpc) 传输层,实现统一的 [Middleware](https://go-kratos.dev/docs/component/middleware/overview) 插件支持;
* [Registry](https://go-kratos.dev/docs/component/registry) :实现统一注册中心接口,可插件化对接各种注册中心;
* [Validation](https://go-kratos.dev/docs/component/middleware/validate): 通过Protobuf统一定义校验规则,并同时适用于HTTP/gRPC服务.
* [SwaggerAPI](https://go-kratos.dev/docs/guide/openapi): 通过集成第三方[Swagger插件](https://github.com/go-kratos/swagger-api) 能够自动生成Swagger API json并启动一个内置的Swagger UI服务.
## Getting Started
### Required
- [go](https://golang.org/dl/)
- [protoc](https://github.com/protocolbuffers/protobuf)
- [protoc-gen-go](https://github.com/protocolbuffers/protobuf-go)
### Installing
##### go install 安装:
```
go install github.com/go-kratos/kratos/cmd/kratos/v2@latest
kratos upgrade
```
##### 源码编译安装:
```
git clone https://github.com/go-kratos/kratos
cd kratos
make install
```
### Create a service
```
# 创建项目模板
kratos new helloworld
cd helloworld
# 拉取项目依赖
go mod download
# 生成proto模板
kratos proto add api/helloworld/helloworld.proto
# 生成proto源码
kratos proto client api/helloworld/helloworld.proto
# 生成server模板
kratos proto server api/helloworld/helloworld.proto -t internal/service
# 生成所有proto源码、wire等等
go generate ./...
# 运行程序
kratos run
```
### Kratos Boot
```
import "github.com/go-kratos/kratos/v2"
import "github.com/go-kratos/kratos/v2/transport/grpc"
import "github.com/go-kratos/kratos/v2/transport/http"
httpSrv := http.NewServer(http.Address(":8000"))
grpcSrv := grpc.NewServer(grpc.Address(":9000"))
app := kratos.New(
kratos.Name("kratos"),
kratos.Version("latest"),
kratos.Server(httpSrv, grpcSrv),
)
app.Run()
```
## Related
* [Docs](https://go-kratos.dev/)
* [Examples](https://github.com/go-kratos/examples)
* [Service Layout](https://github.com/go-kratos/kratos-layout)
## Community
* [Wechat Group](https://github.com/go-kratos/kratos/issues/682)
* [Discord Group](https://discord.gg/BWzJsUJ)
* Website: [go-kratos.dev](https://go-kratos.dev)
* QQ Group: 716486124
## WeChat Official Account
![kratos](docs/images/wechat.png)
## Conventional commits
提交信息的结构应该如下所示:
```text
<type>[optional scope]: <description>
[optional body]
[optional footer(s)]
```
提交信息应按照下面的格式:
- fix: simply describe the problem that has been fixed
- feat(log): simple describe of new features
- deps(examples): simple describe the change of the dependency
- break(http): simple describe the reasons for breaking change
## Sponsors and Backers
![kratos](docs/images/alipay.png)
## License
Kratos is MIT licensed. See the [LICENSE](./LICENSE) file for details.

@ -1,82 +0,0 @@
# Kratos
This document defines the roadmap for Kratos development.
## Features
- [x] Config
- [x] Local Files
- [x] K8s ConfigMap
- [x] Consul
- [x] Etcd
- [x] Nacos
- [x] Registry
- [x] Consul
- [x] Etcd
- [x] K8s
- [x] Nacos
- [x] Encoding
- [x] JSON
- [x] Protobuf
- [x] Transport
- [x] HTTP
- [x] gRPC
- [x] Middleware
- [x] Logging
- [x] metrics
- [x] recovery
- [x] gRPC status
- [x] transport tracing
- [x] Validator
- [x] Authentication
- [x] Ratelimit
- [x] CircuitBreaker
- [x] Metrics
- [x] Prometheus
- [x] DataDog
- [x] Tracing
- [x] HTTP
- [x] TLS
- [x] Client
- [x] Service Registrar
- [ ] javascript/typescript clients
- [x] gRPC
- [x] TLS
- [x] Uarry Handler
- [x] Streaming Handler
- [ ] Cache
- [ ] go-redis
- [x] Event
- [x] Pub/Sub
- [x] Kafka
- [ ] Nats
- [x] Database
- [x] Ent
- [ ] Gorm
## Platform
- [ ] Kratos API
- [ ] Auth
- [ ] Config
- [ ] Registry
- [ ] Events
- [ ] Kratos Runtime
- [ ] Secrets
- [ ] Service-to-Service
- [ ] Publish and Subscribe
- [ ] Observability
- [ ] Controllable
- [ ] Kratos UI
- [ ] Auth
- [ ] Config
- [ ] Services
- [ ] Endpoints
- [ ] Ratelimit
- [ ] CircuitBreaker
- [ ] FaultInjection
- [ ] TrafficPolicy
## Tools
- [x] Kratos
- [x] HTTP Generator
- [ ] API YAML
- [x] Errors Generator

@ -1,19 +0,0 @@
# Security Policy
## Supported Versions
Use this section to tell people about which versions of your project are
currently being supported with security updates.
| Version | Supported |
|-------------|--------------------|
| 2.0.rc1 | :white_check_mark: |
| < 2.0.beta3 | :x: |
## Reporting a Vulnerability
Use this section to tell people how to report a vulnerability.
Tell them where to go, how often they can expect to get an update on a
reported vulnerability, what to expect if the vulnerability is accepted or
declined, etc.

@ -1 +0,0 @@
# API proto

@ -1,365 +0,0 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.28.0
// protoc v3.19.4
// source: metadata/metadata.proto
package metadata
import (
_ "google.golang.org/genproto/googleapis/api/annotations"
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
descriptorpb "google.golang.org/protobuf/types/descriptorpb"
reflect "reflect"
sync "sync"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
type ListServicesRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
}
func (x *ListServicesRequest) Reset() {
*x = ListServicesRequest{}
if protoimpl.UnsafeEnabled {
mi := &file_metadata_metadata_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *ListServicesRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ListServicesRequest) ProtoMessage() {}
func (x *ListServicesRequest) ProtoReflect() protoreflect.Message {
mi := &file_metadata_metadata_proto_msgTypes[0]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use ListServicesRequest.ProtoReflect.Descriptor instead.
func (*ListServicesRequest) Descriptor() ([]byte, []int) {
return file_metadata_metadata_proto_rawDescGZIP(), []int{0}
}
type ListServicesReply struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Services []string `protobuf:"bytes,1,rep,name=services,proto3" json:"services,omitempty"`
Methods []string `protobuf:"bytes,2,rep,name=methods,proto3" json:"methods,omitempty"`
}
func (x *ListServicesReply) Reset() {
*x = ListServicesReply{}
if protoimpl.UnsafeEnabled {
mi := &file_metadata_metadata_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *ListServicesReply) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ListServicesReply) ProtoMessage() {}
func (x *ListServicesReply) ProtoReflect() protoreflect.Message {
mi := &file_metadata_metadata_proto_msgTypes[1]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use ListServicesReply.ProtoReflect.Descriptor instead.
func (*ListServicesReply) Descriptor() ([]byte, []int) {
return file_metadata_metadata_proto_rawDescGZIP(), []int{1}
}
func (x *ListServicesReply) GetServices() []string {
if x != nil {
return x.Services
}
return nil
}
func (x *ListServicesReply) GetMethods() []string {
if x != nil {
return x.Methods
}
return nil
}
type GetServiceDescRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
}
func (x *GetServiceDescRequest) Reset() {
*x = GetServiceDescRequest{}
if protoimpl.UnsafeEnabled {
mi := &file_metadata_metadata_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *GetServiceDescRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*GetServiceDescRequest) ProtoMessage() {}
func (x *GetServiceDescRequest) ProtoReflect() protoreflect.Message {
mi := &file_metadata_metadata_proto_msgTypes[2]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use GetServiceDescRequest.ProtoReflect.Descriptor instead.
func (*GetServiceDescRequest) Descriptor() ([]byte, []int) {
return file_metadata_metadata_proto_rawDescGZIP(), []int{2}
}
func (x *GetServiceDescRequest) GetName() string {
if x != nil {
return x.Name
}
return ""
}
type GetServiceDescReply struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
FileDescSet *descriptorpb.FileDescriptorSet `protobuf:"bytes,1,opt,name=file_desc_set,json=fileDescSet,proto3" json:"file_desc_set,omitempty"`
}
func (x *GetServiceDescReply) Reset() {
*x = GetServiceDescReply{}
if protoimpl.UnsafeEnabled {
mi := &file_metadata_metadata_proto_msgTypes[3]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *GetServiceDescReply) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*GetServiceDescReply) ProtoMessage() {}
func (x *GetServiceDescReply) ProtoReflect() protoreflect.Message {
mi := &file_metadata_metadata_proto_msgTypes[3]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use GetServiceDescReply.ProtoReflect.Descriptor instead.
func (*GetServiceDescReply) Descriptor() ([]byte, []int) {
return file_metadata_metadata_proto_rawDescGZIP(), []int{3}
}
func (x *GetServiceDescReply) GetFileDescSet() *descriptorpb.FileDescriptorSet {
if x != nil {
return x.FileDescSet
}
return nil
}
var File_metadata_metadata_proto protoreflect.FileDescriptor
var file_metadata_metadata_proto_rawDesc = []byte{
0x0a, 0x17, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x2f, 0x6d, 0x65, 0x74, 0x61, 0x64,
0x61, 0x74, 0x61, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0a, 0x6b, 0x72, 0x61, 0x74, 0x6f,
0x73, 0x2e, 0x61, 0x70, 0x69, 0x1a, 0x20, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72,
0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f,
0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f,
0x61, 0x70, 0x69, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e,
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x15, 0x0a, 0x13, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x65, 0x72,
0x76, 0x69, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x49, 0x0a, 0x11,
0x4c, 0x69, 0x73, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x52, 0x65, 0x70, 0x6c,
0x79, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x18, 0x01, 0x20,
0x03, 0x28, 0x09, 0x52, 0x08, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x12, 0x18, 0x0a,
0x07, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07,
0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x73, 0x22, 0x2b, 0x0a, 0x15, 0x47, 0x65, 0x74, 0x53, 0x65,
0x72, 0x76, 0x69, 0x63, 0x65, 0x44, 0x65, 0x73, 0x63, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04,
0x6e, 0x61, 0x6d, 0x65, 0x22, 0x5d, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69,
0x63, 0x65, 0x44, 0x65, 0x73, 0x63, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x46, 0x0a, 0x0d, 0x66,
0x69, 0x6c, 0x65, 0x5f, 0x64, 0x65, 0x73, 0x63, 0x5f, 0x73, 0x65, 0x74, 0x18, 0x01, 0x20, 0x01,
0x28, 0x0b, 0x32, 0x22, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74,
0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70,
0x74, 0x6f, 0x72, 0x53, 0x65, 0x74, 0x52, 0x0b, 0x66, 0x69, 0x6c, 0x65, 0x44, 0x65, 0x73, 0x63,
0x53, 0x65, 0x74, 0x32, 0xdd, 0x01, 0x0a, 0x08, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61,
0x12, 0x61, 0x0a, 0x0c, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73,
0x12, 0x1f, 0x2e, 0x6b, 0x72, 0x61, 0x74, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x4c, 0x69,
0x73, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
0x74, 0x1a, 0x1d, 0x2e, 0x6b, 0x72, 0x61, 0x74, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x4c,
0x69, 0x73, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79,
0x22, 0x11, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0b, 0x12, 0x09, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x69,
0x63, 0x65, 0x73, 0x12, 0x6e, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63,
0x65, 0x44, 0x65, 0x73, 0x63, 0x12, 0x21, 0x2e, 0x6b, 0x72, 0x61, 0x74, 0x6f, 0x73, 0x2e, 0x61,
0x70, 0x69, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x44, 0x65, 0x73,
0x63, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x6b, 0x72, 0x61, 0x74, 0x6f,
0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65,
0x44, 0x65, 0x73, 0x63, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x18, 0x82, 0xd3, 0xe4, 0x93, 0x02,
0x12, 0x12, 0x10, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2f, 0x7b, 0x6e, 0x61,
0x6d, 0x65, 0x7d, 0x42, 0x63, 0x0a, 0x15, 0x63, 0x6f, 0x6d, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75,
0x62, 0x2e, 0x6b, 0x72, 0x61, 0x74, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x50, 0x01, 0x5a, 0x3c,
0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x6f, 0x2d, 0x6b, 0x72,
0x61, 0x74, 0x6f, 0x73, 0x2f, 0x6b, 0x72, 0x61, 0x74, 0x6f, 0x73, 0x2f, 0x76, 0x32, 0x2f, 0x61,
0x70, 0x69, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x6b, 0x72, 0x61, 0x74, 0x6f, 0x73, 0x2f,
0x61, 0x70, 0x69, 0x3b, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xa2, 0x02, 0x09, 0x4b,
0x72, 0x61, 0x74, 0x6f, 0x73, 0x41, 0x50, 0x49, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
file_metadata_metadata_proto_rawDescOnce sync.Once
file_metadata_metadata_proto_rawDescData = file_metadata_metadata_proto_rawDesc
)
func file_metadata_metadata_proto_rawDescGZIP() []byte {
file_metadata_metadata_proto_rawDescOnce.Do(func() {
file_metadata_metadata_proto_rawDescData = protoimpl.X.CompressGZIP(file_metadata_metadata_proto_rawDescData)
})
return file_metadata_metadata_proto_rawDescData
}
var file_metadata_metadata_proto_msgTypes = make([]protoimpl.MessageInfo, 4)
var file_metadata_metadata_proto_goTypes = []interface{}{
(*ListServicesRequest)(nil), // 0: kratos.api.ListServicesRequest
(*ListServicesReply)(nil), // 1: kratos.api.ListServicesReply
(*GetServiceDescRequest)(nil), // 2: kratos.api.GetServiceDescRequest
(*GetServiceDescReply)(nil), // 3: kratos.api.GetServiceDescReply
(*descriptorpb.FileDescriptorSet)(nil), // 4: google.protobuf.FileDescriptorSet
}
var file_metadata_metadata_proto_depIdxs = []int32{
4, // 0: kratos.api.GetServiceDescReply.file_desc_set:type_name -> google.protobuf.FileDescriptorSet
0, // 1: kratos.api.Metadata.ListServices:input_type -> kratos.api.ListServicesRequest
2, // 2: kratos.api.Metadata.GetServiceDesc:input_type -> kratos.api.GetServiceDescRequest
1, // 3: kratos.api.Metadata.ListServices:output_type -> kratos.api.ListServicesReply
3, // 4: kratos.api.Metadata.GetServiceDesc:output_type -> kratos.api.GetServiceDescReply
3, // [3:5] is the sub-list for method output_type
1, // [1:3] is the sub-list for method input_type
1, // [1:1] is the sub-list for extension type_name
1, // [1:1] is the sub-list for extension extendee
0, // [0:1] is the sub-list for field type_name
}
func init() { file_metadata_metadata_proto_init() }
func file_metadata_metadata_proto_init() {
if File_metadata_metadata_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
file_metadata_metadata_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*ListServicesRequest); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_metadata_metadata_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*ListServicesReply); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_metadata_metadata_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*GetServiceDescRequest); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_metadata_metadata_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*GetServiceDescReply); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_metadata_metadata_proto_rawDesc,
NumEnums: 0,
NumMessages: 4,
NumExtensions: 0,
NumServices: 1,
},
GoTypes: file_metadata_metadata_proto_goTypes,
DependencyIndexes: file_metadata_metadata_proto_depIdxs,
MessageInfos: file_metadata_metadata_proto_msgTypes,
}.Build()
File_metadata_metadata_proto = out.File
file_metadata_metadata_proto_rawDesc = nil
file_metadata_metadata_proto_goTypes = nil
file_metadata_metadata_proto_depIdxs = nil
}

@ -1,43 +0,0 @@
syntax = "proto3";
package kratos.api;
import "google/protobuf/descriptor.proto";
import "google/api/annotations.proto";
option go_package = "github.com/go-kratos/kratos/v2/api/proto/kratos/api;metadata";
option java_multiple_files = true;
option java_package = "com.github.kratos.api";
option objc_class_prefix = "KratosAPI";
// Metadata is api definition metadata service.
service Metadata {
// ListServices list the full name of all services.
rpc ListServices (ListServicesRequest) returns (ListServicesReply) {
option (google.api.http) = {
get: "/services",
};
}
// GetServiceDesc get the full fileDescriptorSet of service.
rpc GetServiceDesc (GetServiceDescRequest) returns (GetServiceDescReply) {
option (google.api.http) = {
get: "/services/{name}",
};
}
}
message ListServicesRequest {}
message ListServicesReply {
repeated string services = 1;
repeated string methods = 2;
}
message GetServiceDescRequest {
string name = 1;
}
message GetServiceDescReply {
google.protobuf.FileDescriptorSet file_desc_set = 1;
}

@ -1,145 +0,0 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.2.0
// - protoc v3.19.4
// source: metadata/metadata.proto
package metadata
import (
context "context"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"
)
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
// Requires gRPC-Go v1.32.0 or later.
const _ = grpc.SupportPackageIsVersion7
// MetadataClient is the client API for Metadata service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
type MetadataClient interface {
// ListServices list the full name of all services.
ListServices(ctx context.Context, in *ListServicesRequest, opts ...grpc.CallOption) (*ListServicesReply, error)
// GetServiceDesc get the full fileDescriptorSet of service.
GetServiceDesc(ctx context.Context, in *GetServiceDescRequest, opts ...grpc.CallOption) (*GetServiceDescReply, error)
}
type metadataClient struct {
cc grpc.ClientConnInterface
}
func NewMetadataClient(cc grpc.ClientConnInterface) MetadataClient {
return &metadataClient{cc}
}
func (c *metadataClient) ListServices(ctx context.Context, in *ListServicesRequest, opts ...grpc.CallOption) (*ListServicesReply, error) {
out := new(ListServicesReply)
err := c.cc.Invoke(ctx, "/kratos.api.Metadata/ListServices", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *metadataClient) GetServiceDesc(ctx context.Context, in *GetServiceDescRequest, opts ...grpc.CallOption) (*GetServiceDescReply, error) {
out := new(GetServiceDescReply)
err := c.cc.Invoke(ctx, "/kratos.api.Metadata/GetServiceDesc", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// MetadataServer is the server API for Metadata service.
// All implementations must embed UnimplementedMetadataServer
// for forward compatibility
type MetadataServer interface {
// ListServices list the full name of all services.
ListServices(context.Context, *ListServicesRequest) (*ListServicesReply, error)
// GetServiceDesc get the full fileDescriptorSet of service.
GetServiceDesc(context.Context, *GetServiceDescRequest) (*GetServiceDescReply, error)
mustEmbedUnimplementedMetadataServer()
}
// UnimplementedMetadataServer must be embedded to have forward compatible implementations.
type UnimplementedMetadataServer struct {
}
func (UnimplementedMetadataServer) ListServices(context.Context, *ListServicesRequest) (*ListServicesReply, error) {
return nil, status.Errorf(codes.Unimplemented, "method ListServices not implemented")
}
func (UnimplementedMetadataServer) GetServiceDesc(context.Context, *GetServiceDescRequest) (*GetServiceDescReply, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetServiceDesc not implemented")
}
func (UnimplementedMetadataServer) mustEmbedUnimplementedMetadataServer() {}
// UnsafeMetadataServer may be embedded to opt out of forward compatibility for this service.
// Use of this interface is not recommended, as added methods to MetadataServer will
// result in compilation errors.
type UnsafeMetadataServer interface {
mustEmbedUnimplementedMetadataServer()
}
func RegisterMetadataServer(s grpc.ServiceRegistrar, srv MetadataServer) {
s.RegisterService(&Metadata_ServiceDesc, srv)
}
func _Metadata_ListServices_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(ListServicesRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(MetadataServer).ListServices(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/kratos.api.Metadata/ListServices",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(MetadataServer).ListServices(ctx, req.(*ListServicesRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Metadata_GetServiceDesc_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(GetServiceDescRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(MetadataServer).GetServiceDesc(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/kratos.api.Metadata/GetServiceDesc",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(MetadataServer).GetServiceDesc(ctx, req.(*GetServiceDescRequest))
}
return interceptor(ctx, in, info, handler)
}
// Metadata_ServiceDesc is the grpc.ServiceDesc for Metadata service.
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
var Metadata_ServiceDesc = grpc.ServiceDesc{
ServiceName: "kratos.api.Metadata",
HandlerType: (*MetadataServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "ListServices",
Handler: _Metadata_ListServices_Handler,
},
{
MethodName: "GetServiceDesc",
Handler: _Metadata_GetServiceDesc_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "metadata/metadata.proto",
}

@ -1,109 +0,0 @@
// Code generated by protoc-gen-go-http. DO NOT EDIT.
// versions:
// protoc-gen-go-http v2.3.0
package metadata
import (
context "context"
http "github.com/go-kratos/kratos/v2/transport/http"
binding "github.com/go-kratos/kratos/v2/transport/http/binding"
)
// This is a compile-time assertion to ensure that this generated file
// is compatible with the kratos package it is being compiled against.
var _ = new(context.Context)
var _ = binding.EncodeURL
const _ = http.SupportPackageIsVersion1
type MetadataHTTPServer interface {
GetServiceDesc(context.Context, *GetServiceDescRequest) (*GetServiceDescReply, error)
ListServices(context.Context, *ListServicesRequest) (*ListServicesReply, error)
}
func RegisterMetadataHTTPServer(s *http.Server, srv MetadataHTTPServer) {
r := s.Route("/")
r.GET("/services", _Metadata_ListServices0_HTTP_Handler(srv))
r.GET("/services/{name}", _Metadata_GetServiceDesc0_HTTP_Handler(srv))
}
func _Metadata_ListServices0_HTTP_Handler(srv MetadataHTTPServer) func(ctx http.Context) error {
return func(ctx http.Context) error {
var in ListServicesRequest
if err := ctx.BindQuery(&in); err != nil {
return err
}
http.SetOperation(ctx, "/kratos.api.Metadata/ListServices")
h := ctx.Middleware(func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.ListServices(ctx, req.(*ListServicesRequest))
})
out, err := h(ctx, &in)
if err != nil {
return err
}
reply := out.(*ListServicesReply)
return ctx.Result(200, reply)
}
}
func _Metadata_GetServiceDesc0_HTTP_Handler(srv MetadataHTTPServer) func(ctx http.Context) error {
return func(ctx http.Context) error {
var in GetServiceDescRequest
if err := ctx.BindQuery(&in); err != nil {
return err
}
if err := ctx.BindVars(&in); err != nil {
return err
}
http.SetOperation(ctx, "/kratos.api.Metadata/GetServiceDesc")
h := ctx.Middleware(func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.GetServiceDesc(ctx, req.(*GetServiceDescRequest))
})
out, err := h(ctx, &in)
if err != nil {
return err
}
reply := out.(*GetServiceDescReply)
return ctx.Result(200, reply)
}
}
type MetadataHTTPClient interface {
GetServiceDesc(ctx context.Context, req *GetServiceDescRequest, opts ...http.CallOption) (rsp *GetServiceDescReply, err error)
ListServices(ctx context.Context, req *ListServicesRequest, opts ...http.CallOption) (rsp *ListServicesReply, err error)
}
type MetadataHTTPClientImpl struct {
cc *http.Client
}
func NewMetadataHTTPClient(client *http.Client) MetadataHTTPClient {
return &MetadataHTTPClientImpl{client}
}
func (c *MetadataHTTPClientImpl) GetServiceDesc(ctx context.Context, in *GetServiceDescRequest, opts ...http.CallOption) (*GetServiceDescReply, error) {
var out GetServiceDescReply
pattern := "/services/{name}"
path := binding.EncodeURL(pattern, in, true)
opts = append(opts, http.Operation("/kratos.api.Metadata/GetServiceDesc"))
opts = append(opts, http.PathTemplate(pattern))
err := c.cc.Invoke(ctx, "GET", path, nil, &out, opts...)
if err != nil {
return nil, err
}
return &out, err
}
func (c *MetadataHTTPClientImpl) ListServices(ctx context.Context, in *ListServicesRequest, opts ...http.CallOption) (*ListServicesReply, error) {
var out ListServicesReply
pattern := "/services"
path := binding.EncodeURL(pattern, in, true)
opts = append(opts, http.Operation("/kratos.api.Metadata/ListServices"))
opts = append(opts, http.PathTemplate(pattern))
err := c.cc.Invoke(ctx, "GET", path, nil, &out, opts...)
if err != nil {
return nil, err
}
return &out, err
}

@ -1,206 +0,0 @@
package metadata
import (
"bytes"
"compress/gzip"
"context"
"errors"
"fmt"
"io"
"sort"
"sync"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/reflect/protodesc"
"google.golang.org/protobuf/reflect/protoreflect"
"google.golang.org/protobuf/reflect/protoregistry"
dpb "google.golang.org/protobuf/types/descriptorpb"
"github.com/go-kratos/kratos/v2/log"
)
// Server is api meta server
type Server struct {
UnimplementedMetadataServer
srv *grpc.Server
lock sync.Mutex
services map[string]*dpb.FileDescriptorSet
methods map[string][]string
}
// NewServer create server instance
func NewServer(srv *grpc.Server) *Server {
return &Server{
srv: srv,
services: make(map[string]*dpb.FileDescriptorSet),
methods: make(map[string][]string),
}
}
func (s *Server) load() error {
if len(s.services) > 0 {
return nil
}
if s.srv != nil {
for name, info := range s.srv.GetServiceInfo() {
fd, err := parseMetadata(info.Metadata)
if err != nil {
return fmt.Errorf("invalid service %s metadata err:%v", name, err)
}
protoSet, err := allDependency(fd)
if err != nil {
return err
}
s.services[name] = &dpb.FileDescriptorSet{File: protoSet}
for _, method := range info.Methods {
s.methods[name] = append(s.methods[name], method.Name)
}
}
return nil
}
var err error
protoregistry.GlobalFiles.RangeFiles(func(fd protoreflect.FileDescriptor) bool {
if fd.Services() == nil {
return true
}
for i := 0; i < fd.Services().Len(); i++ {
svc := fd.Services().Get(i)
fdp, e := fileDescriptorProto(fd.Path())
if e != nil {
err = e
return false
}
fdps, e := allDependency(fdp)
if e != nil {
if errors.Is(e, protoregistry.NotFound) {
// Skip this service if one of its dependencies is not found.
continue
}
err = e
return false
}
s.services[string(svc.FullName())] = &dpb.FileDescriptorSet{File: fdps}
if svc.Methods() == nil {
continue
}
for j := 0; j < svc.Methods().Len(); j++ {
method := svc.Methods().Get(j)
s.methods[string(svc.FullName())] = append(s.methods[string(svc.FullName())], string(method.Name()))
}
}
return true
})
return err
}
// ListServices return all services
func (s *Server) ListServices(_ context.Context, _ *ListServicesRequest) (*ListServicesReply, error) {
s.lock.Lock()
defer s.lock.Unlock()
if err := s.load(); err != nil {
return nil, err
}
reply := &ListServicesReply{
Services: make([]string, 0, len(s.services)),
Methods: make([]string, 0, len(s.methods)),
}
for name := range s.services {
reply.Services = append(reply.Services, name)
}
for name, methods := range s.methods {
for _, method := range methods {
reply.Methods = append(reply.Methods, fmt.Sprintf("/%s/%s", name, method))
}
}
sort.Strings(reply.Services)
sort.Strings(reply.Methods)
return reply, nil
}
// GetServiceDesc return service meta by name
func (s *Server) GetServiceDesc(_ context.Context, in *GetServiceDescRequest) (*GetServiceDescReply, error) {
s.lock.Lock()
defer s.lock.Unlock()
if err := s.load(); err != nil {
return nil, err
}
fds, ok := s.services[in.Name]
if !ok {
return nil, status.Errorf(codes.NotFound, "service %s not found", in.Name)
}
return &GetServiceDescReply{FileDescSet: fds}, nil
}
// parseMetadata finds the file descriptor bytes specified meta.
// For SupportPackageIsVersion4, m is the name of the proto file, we
// call proto.FileDescriptor to get the byte slice.
// For SupportPackageIsVersion3, m is a byte slice itself.
func parseMetadata(meta interface{}) (*dpb.FileDescriptorProto, error) {
// Check if meta is the file name.
if fileNameForMeta, ok := meta.(string); ok {
return fileDescriptorProto(fileNameForMeta)
}
// Check if meta is the byte slice.
if enc, ok := meta.([]byte); ok {
return decodeFileDesc(enc)
}
return nil, fmt.Errorf("proto not sumpport metadata: %v", meta)
}
// decodeFileDesc does decompression and unmarshalling on the given
// file descriptor byte slice.
func decodeFileDesc(enc []byte) (*dpb.FileDescriptorProto, error) {
raw, err := decompress(enc)
if err != nil {
return nil, fmt.Errorf("failed to decompress enc: %v", err)
}
fd := new(dpb.FileDescriptorProto)
if err := proto.Unmarshal(raw, fd); err != nil {
return nil, fmt.Errorf("bad descriptor: %v", err)
}
return fd, nil
}
func allDependency(fd *dpb.FileDescriptorProto) ([]*dpb.FileDescriptorProto, error) {
var files []*dpb.FileDescriptorProto
for _, dep := range fd.Dependency {
fdDep, err := fileDescriptorProto(dep)
if err != nil {
log.Warnf("%s", err)
continue
}
temp, err := allDependency(fdDep)
if err != nil {
return nil, err
}
files = append(files, temp...)
}
files = append(files, fd)
return files, nil
}
// decompress does gzip decompression.
func decompress(b []byte) ([]byte, error) {
r, err := gzip.NewReader(bytes.NewReader(b))
if err != nil {
return nil, fmt.Errorf("bad gzipped descriptor: %v", err)
}
out, err := io.ReadAll(r)
if err != nil {
return nil, fmt.Errorf("bad gzipped descriptor: %v", err)
}
return out, nil
}
func fileDescriptorProto(path string) (*dpb.FileDescriptorProto, error) {
fd, err := protoregistry.GlobalFiles.FindFileByPath(path)
if err != nil {
return nil, fmt.Errorf("find proto by path failed, path: %s, err: %s", path, err)
}
fdpb := protodesc.ToFileDescriptorProto(fd)
return fdpb, nil
}

207
app.go

@ -1,207 +0,0 @@
package kratos
import (
"context"
"errors"
"os"
"os/signal"
"sync"
"syscall"
"time"
"github.com/go-kratos/kratos/v2/log"
"github.com/go-kratos/kratos/v2/registry"
"github.com/go-kratos/kratos/v2/transport"
"github.com/google/uuid"
"golang.org/x/sync/errgroup"
)
// AppInfo is application context value.
type AppInfo interface {
ID() string
Name() string
Version() string
Metadata() map[string]string
Endpoint() []string
}
// App is an application components lifecycle manager.
type App struct {
opts options
ctx context.Context
cancel func()
mu sync.Mutex
instance *registry.ServiceInstance
}
// New create an application lifecycle manager.
func New(opts ...Option) *App {
o := options{
ctx: context.Background(),
sigs: []os.Signal{syscall.SIGTERM, syscall.SIGQUIT, syscall.SIGINT},
registrarTimeout: 10 * time.Second,
stopTimeout: 10 * time.Second,
}
if id, err := uuid.NewUUID(); err == nil {
o.id = id.String()
}
for _, opt := range opts {
opt(&o)
}
if o.logger != nil {
log.SetLogger(o.logger)
}
ctx, cancel := context.WithCancel(o.ctx)
return &App{
ctx: ctx,
cancel: cancel,
opts: o,
}
}
// ID returns app instance id.
func (a *App) ID() string { return a.opts.id }
// Name returns service name.
func (a *App) Name() string { return a.opts.name }
// Version returns app version.
func (a *App) Version() string { return a.opts.version }
// Metadata returns service metadata.
func (a *App) Metadata() map[string]string { return a.opts.metadata }
// Endpoint returns endpoints.
func (a *App) Endpoint() []string {
if a.instance != nil {
return a.instance.Endpoints
}
return nil
}
// Run executes all OnStart hooks registered with the application's Lifecycle.
func (a *App) Run() error {
instance, err := a.buildInstance()
if err != nil {
return err
}
a.mu.Lock()
a.instance = instance
a.mu.Unlock()
sctx := NewContext(a.ctx, a)
eg, ctx := errgroup.WithContext(sctx)
wg := sync.WaitGroup{}
for _, fn := range a.opts.beforeStart {
if err = fn(sctx); err != nil {
return err
}
}
for _, srv := range a.opts.servers {
srv := srv
eg.Go(func() error {
<-ctx.Done() // wait for stop signal
stopCtx, cancel := context.WithTimeout(NewContext(a.opts.ctx, a), a.opts.stopTimeout)
defer cancel()
return srv.Stop(stopCtx)
})
wg.Add(1)
eg.Go(func() error {
wg.Done() // here is to ensure server start has begun running before register, so defer is not needed
return srv.Start(sctx)
})
}
wg.Wait()
if a.opts.registrar != nil {
rctx, rcancel := context.WithTimeout(ctx, a.opts.registrarTimeout)
defer rcancel()
if err = a.opts.registrar.Register(rctx, instance); err != nil {
return err
}
}
for _, fn := range a.opts.afterStart {
if err = fn(sctx); err != nil {
return err
}
}
c := make(chan os.Signal, 1)
signal.Notify(c, a.opts.sigs...)
eg.Go(func() error {
select {
case <-ctx.Done():
return nil
case <-c:
return a.Stop()
}
})
if err = eg.Wait(); err != nil && !errors.Is(err, context.Canceled) {
return err
}
for _, fn := range a.opts.afterStop {
err = fn(sctx)
}
return err
}
// Stop gracefully stops the application.
func (a *App) Stop() (err error) {
sctx := NewContext(a.ctx, a)
for _, fn := range a.opts.beforeStop {
err = fn(sctx)
}
a.mu.Lock()
instance := a.instance
a.mu.Unlock()
if a.opts.registrar != nil && instance != nil {
ctx, cancel := context.WithTimeout(NewContext(a.ctx, a), a.opts.registrarTimeout)
defer cancel()
if err = a.opts.registrar.Deregister(ctx, instance); err != nil {
return err
}
}
if a.cancel != nil {
a.cancel()
}
return err
}
func (a *App) buildInstance() (*registry.ServiceInstance, error) {
endpoints := make([]string, 0, len(a.opts.endpoints))
for _, e := range a.opts.endpoints {
endpoints = append(endpoints, e.String())
}
if len(endpoints) == 0 {
for _, srv := range a.opts.servers {
if r, ok := srv.(transport.Endpointer); ok {
e, err := r.Endpoint()
if err != nil {
return nil, err
}
endpoints = append(endpoints, e.String())
}
}
}
return &registry.ServiceInstance{
ID: a.opts.id,
Name: a.opts.name,
Version: a.opts.version,
Metadata: a.opts.metadata,
Endpoints: endpoints,
}, nil
}
type appKey struct{}
// NewContext returns a new Context that carries value.
func NewContext(ctx context.Context, s AppInfo) context.Context {
return context.WithValue(ctx, appKey{}, s)
}
// FromContext returns the Transport value stored in ctx, if any.
func FromContext(ctx context.Context) (s AppInfo, ok bool) {
s, ok = ctx.Value(appKey{}).(AppInfo)
return
}

@ -1,285 +0,0 @@
package kratos
import (
"context"
"errors"
"net/url"
"reflect"
"sync"
"testing"
"time"
"github.com/go-kratos/kratos/v2/registry"
"github.com/go-kratos/kratos/v2/transport/grpc"
"github.com/go-kratos/kratos/v2/transport/http"
)
type mockRegistry struct {
lk sync.Mutex
service map[string]*registry.ServiceInstance
}
func (r *mockRegistry) Register(_ context.Context, service *registry.ServiceInstance) error {
if service == nil || service.ID == "" {
return errors.New("no service id")
}
r.lk.Lock()
defer r.lk.Unlock()
r.service[service.ID] = service
return nil
}
// Deregister the registration.
func (r *mockRegistry) Deregister(_ context.Context, service *registry.ServiceInstance) error {
r.lk.Lock()
defer r.lk.Unlock()
if r.service[service.ID] == nil {
return errors.New("deregister service not found")
}
delete(r.service, service.ID)
return nil
}
func TestApp(t *testing.T) {
hs := http.NewServer()
gs := grpc.NewServer()
app := New(
Name("kratos"),
Version("v1.0.0"),
Server(hs, gs),
BeforeStart(func(_ context.Context) error {
t.Log("BeforeStart...")
return nil
}),
BeforeStop(func(_ context.Context) error {
t.Log("BeforeStop...")
return nil
}),
AfterStart(func(_ context.Context) error {
t.Log("AfterStart...")
return nil
}),
AfterStop(func(_ context.Context) error {
t.Log("AfterStop...")
return nil
}),
Registrar(&mockRegistry{service: make(map[string]*registry.ServiceInstance)}),
)
time.AfterFunc(time.Second, func() {
_ = app.Stop()
})
if err := app.Run(); err != nil {
t.Fatal(err)
}
}
func TestApp_ID(t *testing.T) {
v := "123"
o := New(ID(v))
if !reflect.DeepEqual(v, o.ID()) {
t.Fatalf("o.ID():%s is not equal to v:%s", o.ID(), v)
}
}
func TestApp_Name(t *testing.T) {
v := "123"
o := New(Name(v))
if !reflect.DeepEqual(v, o.Name()) {
t.Fatalf("o.Name():%s is not equal to v:%s", o.Name(), v)
}
}
func TestApp_Version(t *testing.T) {
v := "123"
o := New(Version(v))
if !reflect.DeepEqual(v, o.Version()) {
t.Fatalf("o.Version():%s is not equal to v:%s", o.Version(), v)
}
}
func TestApp_Metadata(t *testing.T) {
v := map[string]string{
"a": "1",
"b": "2",
}
o := New(Metadata(v))
if !reflect.DeepEqual(v, o.Metadata()) {
t.Fatalf("o.Metadata():%s is not equal to v:%s", o.Metadata(), v)
}
}
func TestApp_Endpoint(t *testing.T) {
v := []string{"https://go-kratos.dev", "localhost"}
var endpoints []*url.URL
for _, urlStr := range v {
if endpoint, err := url.Parse(urlStr); err != nil {
t.Errorf("invalid endpoint:%v", urlStr)
} else {
endpoints = append(endpoints, endpoint)
}
}
o := New(Endpoint(endpoints...))
if instance, err := o.buildInstance(); err != nil {
t.Error("build instance failed")
} else {
o.instance = instance
}
if !reflect.DeepEqual(o.Endpoint(), v) {
t.Errorf("Endpoint() = %v, want %v", o.Endpoint(), v)
}
}
func TestApp_buildInstance(t *testing.T) {
want := struct {
id string
name string
version string
metadata map[string]string
endpoints []string
}{
id: "1",
name: "kratos",
version: "v1.0.0",
metadata: map[string]string{
"a": "1",
"b": "2",
},
endpoints: []string{"https://go-kratos.dev", "localhost"},
}
var endpoints []*url.URL
for _, urlStr := range want.endpoints {
if endpoint, err := url.Parse(urlStr); err != nil {
t.Errorf("invalid endpoint:%v", urlStr)
} else {
endpoints = append(endpoints, endpoint)
}
}
app := New(
ID(want.id),
Name(want.name),
Version(want.version),
Metadata(want.metadata),
Endpoint(endpoints...),
)
if got, err := app.buildInstance(); err != nil {
t.Error("build got failed")
} else {
if got.ID != want.id {
t.Errorf("ID() = %v, want %v", got.ID, want.id)
}
if got.Name != want.name {
t.Errorf("Name() = %v, want %v", got.Name, want.name)
}
if got.Version != want.version {
t.Errorf("Version() = %v, want %v", got.Version, want.version)
}
if !reflect.DeepEqual(got.Endpoints, want.endpoints) {
t.Errorf("Endpoint() = %v, want %v", got.Endpoints, want.endpoints)
}
if !reflect.DeepEqual(got.Metadata, want.metadata) {
t.Errorf("Metadata() = %v, want %v", got.Metadata, want.metadata)
}
}
}
func TestApp_Context(t *testing.T) {
type fields struct {
id string
version string
name string
instance *registry.ServiceInstance
metadata map[string]string
want struct {
id string
version string
name string
endpoint []string
metadata map[string]string
}
}
tests := []fields{
{
id: "1",
name: "kratos-v1",
instance: &registry.ServiceInstance{Endpoints: []string{"https://go-kratos.dev", "localhost"}},
metadata: map[string]string{},
version: "v1",
want: struct {
id string
version string
name string
endpoint []string
metadata map[string]string
}{
id: "1", version: "v1", name: "kratos-v1", endpoint: []string{"https://go-kratos.dev", "localhost"},
metadata: map[string]string{},
},
},
{
id: "2",
name: "kratos-v2",
instance: &registry.ServiceInstance{Endpoints: []string{"test"}},
metadata: map[string]string{"kratos": "https://github.com/go-kratos/kratos"},
version: "v2",
want: struct {
id string
version string
name string
endpoint []string
metadata map[string]string
}{
id: "2", version: "v2", name: "kratos-v2", endpoint: []string{"test"},
metadata: map[string]string{"kratos": "https://github.com/go-kratos/kratos"},
},
},
{
id: "3",
name: "kratos-v3",
instance: nil,
metadata: make(map[string]string),
version: "v3",
want: struct {
id string
version string
name string
endpoint []string
metadata map[string]string
}{
id: "3", version: "v3", name: "kratos-v3", endpoint: nil,
metadata: map[string]string{},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
a := &App{
opts: options{id: tt.id, name: tt.name, metadata: tt.metadata, version: tt.version},
ctx: context.Background(),
cancel: nil,
instance: tt.instance,
}
ctx := NewContext(context.Background(), a)
if got, ok := FromContext(ctx); ok {
if got.ID() != tt.want.id {
t.Errorf("ID() = %v, want %v", got.ID(), tt.want.id)
}
if got.Name() != tt.want.name {
t.Errorf("Name() = %v, want %v", got.Name(), tt.want.name)
}
if got.Version() != tt.want.version {
t.Errorf("Version() = %v, want %v", got.Version(), tt.want.version)
}
if !reflect.DeepEqual(got.Endpoint(), tt.want.endpoint) {
t.Errorf("Endpoint() = %v, want %v", got.Endpoint(), tt.want.endpoint)
}
if !reflect.DeepEqual(got.Metadata(), tt.want.metadata) {
t.Errorf("Metadata() = %v, want %v", got.Metadata(), tt.want.metadata)
}
} else {
t.Errorf("ok() = %v, want %v", ok, true)
}
})
}
}

@ -1,15 +0,0 @@
module github.com/go-kratos/kratos/cmd/kratos/v2
go 1.16
require (
github.com/AlecAivazis/survey/v2 v2.3.7
github.com/emicklei/proto v1.10.0
github.com/fatih/color v1.13.0
github.com/mattn/go-colorable v0.1.12 // indirect
github.com/spf13/cobra v1.4.0
github.com/stretchr/testify v1.7.0 // indirect
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4
golang.org/x/text v0.4.0
gopkg.in/yaml.v3 v3.0.0 // indirect
)

@ -1,79 +0,0 @@
github.com/AlecAivazis/survey/v2 v2.3.7 h1:6I/u8FvytdGsgonrYsVn2t8t4QiRnh6QSTqkkhIiSjQ=
github.com/AlecAivazis/survey/v2 v2.3.7/go.mod h1:xUTIdE4KCOIjsBAE1JYsUPoCqYdZ1reCfTwbto0Fduo=
github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2 h1:+vx7roKuyA63nhn5WAunQHLTznkw5W8b1Xc0dNjp83s=
github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2/go.mod h1:HBCaDeC1lPdgDeDbhX8XFpy1jqjK0IBG8W5K+xYqA0w=
github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/creack/pty v1.1.17 h1:QeVUsEDNrLBW4tMgZHvxy18sKtr6VI492kBhUfhDJNI=
github.com/creack/pty v1.1.17/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
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/emicklei/proto v1.10.0 h1:pDGyFRVV5RvV+nkBK9iy3q67FBy9Xa7vwrOTE+g5aGw=
github.com/emicklei/proto v1.10.0/go.mod h1:rn1FgRS/FANiZdD2djyH7TMA9jdRDcYQ9IEN9yvjX0A=
github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec h1:qv2VnGeEQHchGaZ/u7lxST/RaJw+cv273q79D81Xbog=
github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec/go.mod h1:Q48J4R4DvxnHolD5P8pOtXigYlRuPLGl6moFx3ulM68=
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40=
github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
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/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4=
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
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/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/spf13/cobra v1.4.0 h1:y+wJpx64xcgO1V+RcnwW0LEHxTKRi2ZDPSBjWnrg88Q=
github.com/spf13/cobra v1.4.0/go.mod h1:Wo4iy3BUC+X2Fybo0PDqwJIv3dNRiZLHQymsfxlB84g=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
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-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f h1:v4INt8xihDGvnrfjMDVXGxw9wrfxYyCjk0KbXjhR55s=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/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 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg=
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
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 h1:hjy8E9ON/egN1tAYqKb61G10WtihqetD4sz2H+8nIeA=
gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

@ -1,28 +0,0 @@
//go:build go1.17
// +build go1.17
package base
import (
"fmt"
"os"
"os/exec"
"strings"
)
// GoInstall go get path.
func GoInstall(path ...string) error {
for _, p := range path {
if !strings.Contains(p, "@") {
p += "@latest"
}
fmt.Printf("go install %s\n", p)
cmd := exec.Command("go", "install", p)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
return err
}
}
return nil
}

@ -1,24 +0,0 @@
//go:build !go1.17
// +build !go1.17
package base
import (
"fmt"
"os"
"os/exec"
)
// GoInstall go get path.
func GoInstall(path ...string) error {
for _, p := range path {
fmt.Printf("go get -u %s\n", p)
cmd := exec.Command("go", "get", "-u", p)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
return err
}
}
return nil
}

@ -1,62 +0,0 @@
package base
import (
"bufio"
"bytes"
"os"
"os/exec"
"path/filepath"
"strings"
"golang.org/x/mod/modfile"
)
// ModulePath returns go module path.
func ModulePath(filename string) (string, error) {
modBytes, err := os.ReadFile(filename)
if err != nil {
return "", err
}
return modfile.ModulePath(modBytes), nil
}
// ModuleVersion returns module version.
func ModuleVersion(path string) (string, error) {
stdout := &bytes.Buffer{}
fd := exec.Command("go", "mod", "graph")
fd.Stdout = stdout
fd.Stderr = stdout
if err := fd.Run(); err != nil {
return "", err
}
rd := bufio.NewReader(stdout)
for {
line, _, err := rd.ReadLine()
if err != nil {
return "", err
}
str := string(line)
i := strings.Index(str, "@")
if strings.Contains(str, path+"@") && i != -1 {
return path + str[i:], nil
}
}
}
// KratosMod returns kratos mod.
func KratosMod() string {
// go 1.15+ read from env GOMODCACHE
cacheOut, _ := exec.Command("go", "env", "GOMODCACHE").Output()
cachePath := strings.Trim(string(cacheOut), "\n")
pathOut, _ := exec.Command("go", "env", "GOPATH").Output()
gopath := strings.Trim(string(pathOut), "\n")
if cachePath == "" {
cachePath = filepath.Join(gopath, "pkg", "mod")
}
if path, err := ModuleVersion("github.com/go-kratos/kratos/v2"); err == nil {
// $GOPATH/pkg/mod/github.com/go-kratos/kratos@v2
return filepath.Join(cachePath, path)
}
// $GOPATH/src/github.com/go-kratos/kratos
return filepath.Join(gopath, "src", "github.com", "go-kratos", "kratos")
}

@ -1,43 +0,0 @@
package base
import (
"os"
"testing"
)
func TestModuleVersion(t *testing.T) {
v, err := ModuleVersion("golang.org/x/mod")
if err != nil {
t.Fatal(err)
}
t.Log(v)
}
func TestModulePath(t *testing.T) {
if err := os.Mkdir("/tmp/test_mod", os.ModePerm); err != nil {
t.Fatal(err)
}
f, err := os.Create("/tmp/test_mod/go.mod")
if err != nil {
t.Fatal(err)
}
mod := `module github.com/go-kratos/kratos/v2
go 1.16`
_, err = f.WriteString(mod)
if err != nil {
t.Fatal(err)
}
p, err := ModulePath("/tmp/test_mod/go.mod")
if err != nil {
t.Fatal(err)
}
if p != "github.com/go-kratos/kratos/v2" {
t.Fatalf("want: %s, got: %s", "github.com/go-kratos/kratos/v2", p)
}
t.Cleanup(func() { os.RemoveAll("/tmp/test_mod") })
}

@ -1,108 +0,0 @@
package base
import (
"bytes"
"fmt"
"log"
"os"
"path/filepath"
"strings"
"github.com/fatih/color"
)
func kratosHome() string {
dir, err := os.UserHomeDir()
if err != nil {
log.Fatal(err)
}
home := filepath.Join(dir, ".kratos")
if _, err := os.Stat(home); os.IsNotExist(err) {
if err := os.MkdirAll(home, 0o700); err != nil {
log.Fatal(err)
}
}
return home
}
func kratosHomeWithDir(dir string) string {
home := filepath.Join(kratosHome(), dir)
if _, err := os.Stat(home); os.IsNotExist(err) {
if err := os.MkdirAll(home, 0o700); err != nil {
log.Fatal(err)
}
}
return home
}
func copyFile(src, dst string, replaces []string) error {
srcinfo, err := os.Stat(src)
if err != nil {
return err
}
buf, err := os.ReadFile(src)
if err != nil {
return err
}
var old string
for i, next := range replaces {
if i%2 == 0 {
old = next
continue
}
buf = bytes.ReplaceAll(buf, []byte(old), []byte(next))
}
return os.WriteFile(dst, buf, srcinfo.Mode())
}
func copyDir(src, dst string, replaces, ignores []string) error {
srcinfo, err := os.Stat(src)
if err != nil {
return err
}
err = os.MkdirAll(dst, srcinfo.Mode())
if err != nil {
return err
}
fds, err := os.ReadDir(src)
if err != nil {
return err
}
for _, fd := range fds {
if hasSets(fd.Name(), ignores) {
continue
}
srcfp := filepath.Join(src, fd.Name())
dstfp := filepath.Join(dst, fd.Name())
var e error
if fd.IsDir() {
e = copyDir(srcfp, dstfp, replaces, ignores)
} else {
e = copyFile(srcfp, dstfp, replaces)
}
if e != nil {
return e
}
}
return nil
}
func hasSets(name string, sets []string) bool {
for _, ig := range sets {
if ig == name {
return true
}
}
return false
}
func Tree(path string, dir string) {
_ = filepath.Walk(path, func(path string, info os.FileInfo, err error) error {
if err == nil && info != nil && !info.IsDir() {
fmt.Printf("%s %s (%v bytes)\n", color.GreenString("CREATED"), strings.Replace(path, dir+"/", "", -1), info.Size())
}
return nil
})
}

@ -1,126 +0,0 @@
package base
import (
"context"
"fmt"
"net"
"os"
"os/exec"
"path"
"path/filepath"
"strings"
)
var unExpandVarPath = []string{"~", ".", ".."}
// Repo is git repository manager.
type Repo struct {
url string
home string
branch string
}
func repoDir(url string) string {
vcsURL, err := ParseVCSUrl(url)
if err != nil {
return url
}
// check host contains port
host, _, err := net.SplitHostPort(vcsURL.Host)
if err != nil {
host = vcsURL.Host
}
for _, p := range unExpandVarPath {
host = strings.TrimLeft(host, p)
}
dir := path.Base(path.Dir(vcsURL.Path))
url = fmt.Sprintf("%s/%s", host, dir)
return url
}
// NewRepo new a repository manager.
func NewRepo(url string, branch string) *Repo {
return &Repo{
url: url,
home: kratosHomeWithDir("repo/" + repoDir(url)),
branch: branch,
}
}
// Path returns the repository cache path.
func (r *Repo) Path() string {
start := strings.LastIndex(r.url, "/")
end := strings.LastIndex(r.url, ".git")
if end == -1 {
end = len(r.url)
}
var branch string
if r.branch == "" {
branch = "@main"
} else {
branch = "@" + r.branch
}
return path.Join(r.home, r.url[start+1:end]+branch)
}
// Pull fetch the repository from remote url.
func (r *Repo) Pull(ctx context.Context) error {
cmd := exec.CommandContext(ctx, "git", "symbolic-ref", "HEAD")
cmd.Dir = r.Path()
_, err := cmd.CombinedOutput()
if err != nil {
return err
}
cmd = exec.CommandContext(ctx, "git", "pull")
cmd.Dir = r.Path()
out, err := cmd.CombinedOutput()
fmt.Println(string(out))
if err != nil {
return err
}
return err
}
// Clone clones the repository to cache path.
func (r *Repo) Clone(ctx context.Context) error {
if _, err := os.Stat(r.Path()); !os.IsNotExist(err) {
return r.Pull(ctx)
}
var cmd *exec.Cmd
if r.branch == "" {
cmd = exec.CommandContext(ctx, "git", "clone", r.url, r.Path())
} else {
cmd = exec.CommandContext(ctx, "git", "clone", "-b", r.branch, r.url, r.Path())
}
out, err := cmd.CombinedOutput()
fmt.Println(string(out))
if err != nil {
return err
}
return nil
}
// CopyTo copies the repository to project path.
func (r *Repo) CopyTo(ctx context.Context, to string, modPath string, ignores []string) error {
if err := r.Clone(ctx); err != nil {
return err
}
mod, err := ModulePath(filepath.Join(r.Path(), "go.mod"))
if err != nil {
return err
}
return copyDir(r.Path(), to, []string{mod, modPath}, ignores)
}
// CopyToV2 copies the repository to project path
func (r *Repo) CopyToV2(ctx context.Context, to string, modPath string, ignores, replaces []string) error {
if err := r.Clone(ctx); err != nil {
return err
}
mod, err := ModulePath(filepath.Join(r.Path(), "go.mod"))
if err != nil {
return err
}
replaces = append([]string{mod, modPath}, replaces...)
return copyDir(r.Path(), to, replaces, ignores)
}

@ -1,51 +0,0 @@
package base
import (
"context"
"os"
"testing"
)
func TestRepo(t *testing.T) {
urls := []string{
// ssh://[user@]host.xz[:port]/path/to/repo.git/
"ssh://git@github.com:7875/go-kratos/kratos.git",
// git://host.xz[:port]/path/to/repo.git/
"git://github.com:7875/go-kratos/kratos.git",
// http[s]://host.xz[:port]/path/to/repo.git/
"https://github.com:7875/go-kratos/kratos.git",
// ftp[s]://host.xz[:port]/path/to/repo.git/
"ftps://github.com:7875/go-kratos/kratos.git",
//[user@]host.xz:path/to/repo.git/
"git@github.com:go-kratos/kratos.git",
// ssh://[user@]host.xz[:port]/~[user]/path/to/repo.git/
"ssh://git@github.com:7875/go-kratos/kratos.git",
// git://host.xz[:port]/~[user]/path/to/repo.git/
"git://github.com:7875/go-kratos/kratos.git",
//[user@]host.xz:/~[user]/path/to/repo.git/
"git@github.com:go-kratos/kratos.git",
///path/to/repo.git/
"//github.com/go-kratos/kratos.git",
// file:///path/to/repo.git/
"file://./github.com/go-kratos/kratos.git",
}
for _, url := range urls {
dir := repoDir(url)
if dir != "github.com/go-kratos" && dir != "/go-kratos" {
t.Fatal(url, "repoDir test failed", dir)
}
}
}
func TestRepoClone(t *testing.T) {
r := NewRepo("https://github.com/go-kratos/service-layout.git", "")
if err := r.Clone(context.Background()); err != nil {
t.Fatal(err)
}
if err := r.CopyTo(context.Background(), "/tmp/test_repo", "github.com/go-kratos/kratos-layout", nil); err != nil {
t.Fatal(err)
}
t.Cleanup(func() {
os.RemoveAll("/tmp/test_repo")
})
}

@ -1,58 +0,0 @@
package base
import (
"errors"
"net/url"
"regexp"
"strings"
)
var (
scpSyntaxRe = regexp.MustCompile(`^(\w+)@([\w.-]+):(.*)$`)
scheme = []string{"git", "https", "http", "git+ssh", "ssh", "file", "ftp", "ftps"}
)
// ParseVCSUrl ref https://github.com/golang/go/blob/master/src/cmd/go/internal/vcs/vcs.go
// see https://go-review.googlesource.com/c/go/+/12226/
// git url define https://git-scm.com/docs/git-clone#_git_urls
func ParseVCSUrl(repo string) (*url.URL, error) {
var (
repoURL *url.URL
err error
)
if m := scpSyntaxRe.FindStringSubmatch(repo); m != nil {
// Match SCP-like syntax and convert it to a URL.
// Eg, "git@github.com:user/repo" becomes
// "ssh://git@github.com/user/repo".
repoURL = &url.URL{
Scheme: "ssh",
User: url.User(m[1]),
Host: m[2],
Path: m[3],
}
} else {
if !strings.Contains(repo, "//") {
repo = "//" + repo
}
if strings.HasPrefix(repo, "//git@") {
repo = "ssh:" + repo
} else if strings.HasPrefix(repo, "//") {
repo = "https:" + repo
}
repoURL, err = url.Parse(repo)
if err != nil {
return nil, err
}
}
// Iterate over insecure schemes too, because this function simply
// reports the state of the repo. If we can't see insecure schemes then
// we can't report the actual repo URL.
for _, s := range scheme {
if repoURL.Scheme == s {
return repoURL, nil
}
}
return nil, errors.New("unable to parse repo url")
}

@ -1,55 +0,0 @@
package base
import (
"net"
"strings"
"testing"
)
func TestParseVCSUrl(t *testing.T) {
repos := []string{
// ssh://[user@]host.xz[:port]/path/to/repo.git/
"ssh://git@github.com:7875/go-kratos/kratos.git",
// git://host.xz[:port]/path/to/repo.git/
"git://github.com:7875/go-kratos/kratos.git",
// http[s]://host.xz[:port]/path/to/repo.git/
"https://github.com:7875/go-kratos/kratos.git",
// ftp[s]://host.xz[:port]/path/to/repo.git/
"ftps://github.com:7875/go-kratos/kratos.git",
//[user@]host.xz:path/to/repo.git/
"git@github.com:go-kratos/kratos.git",
// ssh://[user@]host.xz[:port]/~[user]/path/to/repo.git/
"ssh://git@github.com:7875/go-kratos/kratos.git",
// git://host.xz[:port]/~[user]/path/to/repo.git/
"git://github.com:7875/go-kratos/kratos.git",
//[user@]host.xz:/~[user]/path/to/repo.git/
"git@github.com:go-kratos/kratos.git",
///path/to/repo.git/
"~/go-kratos/kratos.git",
// file:///path/to/repo.git/
"file://~/go-kratos/kratos.git",
}
for _, repo := range repos {
url, err := ParseVCSUrl(repo)
if err != nil {
t.Fatal(repo, err)
}
urlPath := strings.TrimLeft(url.Path, "/")
if urlPath != "go-kratos/kratos.git" {
t.Fatal(repo, "parse url failed", urlPath)
}
}
}
func TestParseSsh(t *testing.T) {
repo := "ssh://git@github.com:7875/go-kratos/kratos.git"
url, err := ParseVCSUrl(repo)
if err != nil {
t.Fatal(err)
}
host, _, err := net.SplitHostPort(url.Host)
if err != nil {
host = url.Host
}
t.Log(host, url.Path)
}

@ -1,45 +0,0 @@
package change
import (
"fmt"
"os"
"github.com/spf13/cobra"
)
// CmdChange is kratos change log tool
var CmdChange = &cobra.Command{
Use: "changelog",
Short: "Get a kratos change log",
Long: "Get a kratos release or commits info. Example: kratos changelog dev or kratos changelog {version}",
Run: run,
}
var (
token string
repoURL string
)
func init() {
if repoURL = os.Getenv("KRATOS_REPO"); repoURL == "" {
repoURL = "https://github.com/go-kratos/kratos.git"
}
CmdChange.Flags().StringVarP(&repoURL, "repo-url", "r", repoURL, "github repo")
token = os.Getenv("GITHUB_TOKEN")
}
func run(_ *cobra.Command, args []string) {
owner, repo := ParseGithubURL(repoURL)
api := GithubAPI{Owner: owner, Repo: repo, Token: token}
version := "latest"
if len(args) > 0 {
version = args[0]
}
if version == "dev" {
info := api.GetCommitsInfo()
fmt.Print(ParseCommitsInfo(info))
return
}
info := api.GetReleaseInfo(version)
fmt.Print(ParseReleaseInfo(info))
}

@ -1,213 +0,0 @@
package change
import (
"encoding/json"
"errors"
"fmt"
"io"
"net/http"
"os"
"regexp"
"strings"
"time"
)
type ReleaseInfo struct {
Author struct {
Login string `json:"login"`
} `json:"author"`
PublishedAt string `json:"published_at"`
Body string `json:"body"`
HTMLURL string `json:"html_url"`
}
type CommitInfo struct {
Commit struct {
Message string `json:"message"`
} `json:"commit"`
}
type ErrorInfo struct {
Message string
}
type GithubAPI struct {
Owner string
Repo string
Token string
}
// GetReleaseInfo for getting kratos release info.
func (g *GithubAPI) GetReleaseInfo(version string) ReleaseInfo {
api := fmt.Sprintf("https://api.github.com/repos/%s/%s/releases/latest", g.Owner, g.Repo)
if version != "latest" {
api = fmt.Sprintf("https://api.github.com/repos/%s/%s/releases/tags/%s", g.Owner, g.Repo, version)
}
resp, code := requestGithubAPI(api, http.MethodGet, nil, g.Token)
if code != http.StatusOK {
printGithubErrorInfo(resp)
}
releaseInfo := ReleaseInfo{}
err := json.Unmarshal(resp, &releaseInfo)
if err != nil {
fatal(err)
}
return releaseInfo
}
// GetCommitsInfo for getting kratos commits info.
func (g *GithubAPI) GetCommitsInfo() []CommitInfo {
info := g.GetReleaseInfo("latest")
page := 1
prePage := 100
var list []CommitInfo
for {
url := fmt.Sprintf("https://api.github.com/repos/%s/%s/commits?pre_page=%d&page=%d&since=%s", g.Owner, g.Repo, prePage, page, info.PublishedAt)
resp, code := requestGithubAPI(url, http.MethodGet, nil, g.Token)
if code != http.StatusOK {
printGithubErrorInfo(resp)
}
var res []CommitInfo
err := json.Unmarshal(resp, &res)
if err != nil {
fatal(err)
}
list = append(list, res...)
if len(res) < prePage {
break
}
page++
}
return list
}
func printGithubErrorInfo(body []byte) {
errorInfo := &ErrorInfo{}
err := json.Unmarshal(body, errorInfo)
if err != nil {
fatal(err)
}
fatal(errors.New(errorInfo.Message))
}
func requestGithubAPI(url string, method string, body io.Reader, token string) ([]byte, int) {
cli := &http.Client{Timeout: 60 * time.Second}
request, err := http.NewRequest(method, url, body)
if err != nil {
fatal(err)
}
if token != "" {
request.Header.Add("Authorization", token)
}
resp, err := cli.Do(request)
if err != nil {
fatal(err)
}
defer resp.Body.Close()
resBody, err := io.ReadAll(resp.Body)
if err != nil {
fatal(err)
}
return resBody, resp.StatusCode
}
func ParseCommitsInfo(info []CommitInfo) string {
group := map[string][]string{
"fix": {},
"feat": {},
"deps": {},
"break": {},
"chore": {},
"other": {},
}
for _, commitInfo := range info {
msg := commitInfo.Commit.Message
index := strings.Index(fmt.Sprintf("%q", msg), `\n`)
if index != -1 {
msg = msg[:index-1]
}
prefix := []string{"fix", "feat", "deps", "break", "chore"}
var matched bool
for _, v := range prefix {
msg = strings.TrimPrefix(msg, " ")
if strings.HasPrefix(msg, v) {
group[v] = append(group[v], msg)
matched = true
}
}
if !matched {
group["other"] = append(group["other"], msg)
}
}
md := make(map[string]string)
for key, value := range group {
var text string
switch key {
case "break":
text = "### Breaking Changes\n"
case "deps":
text = "### Dependencies\n"
case "feat":
text = "### New Features\n"
case "fix":
text = "### Bug Fixes\n"
case "chore":
text = "### Chores\n"
case "other":
text = "### Others\n"
}
if len(value) == 0 {
continue
}
md[key] += text
for _, value := range value {
md[key] += fmt.Sprintf("- %s\n", value)
}
}
return fmt.Sprint(md["break"], md["deps"], md["feat"], md["fix"], md["chore"], md["other"])
}
func ParseReleaseInfo(info ReleaseInfo) string {
reg := regexp.MustCompile(`(?m)^\s*$[\r\n]*|[\r\n]+\s+\z|<[\S\s]+?>`)
body := reg.ReplaceAll([]byte(info.Body), []byte(""))
if string(body) == "" {
body = []byte("no release info")
}
splitters := "--------------------------------------------"
return fmt.Sprintf(
"Author: %s\nDate: %s\nUrl: %s\n\n%s\n\n%s\n\n%s\n",
info.Author.Login,
info.PublishedAt,
info.HTMLURL,
splitters,
body,
splitters,
)
}
func ParseGithubURL(url string) (owner string, repo string) {
var start int
start = strings.Index(url, "//")
if start == -1 {
start = strings.Index(url, ":") + 1
} else {
start += 2
}
end := strings.LastIndex(url, "/")
gitIndex := strings.LastIndex(url, ".git")
if gitIndex == -1 {
repo = url[strings.LastIndex(url, "/")+1:]
} else {
repo = url[strings.LastIndex(url, "/")+1 : gitIndex]
}
tmp := url[start:end]
owner = tmp[strings.Index(tmp, "/")+1:]
return
}
func fatal(err error) {
fmt.Fprintf(os.Stderr, "\033[31mERROR: %s\033[m\n", err)
os.Exit(1)
}

@ -1,25 +0,0 @@
package change
import "testing"
func TestParseGithubURL(t *testing.T) {
urls := []struct {
url string
owner string
repo string
}{
{"https://github.com/go-kratos/kratos.git", "go-kratos", "kratos"},
{"https://github.com/go-kratos/kratos", "go-kratos", "kratos"},
{"git@github.com:go-kratos/kratos.git", "go-kratos", "kratos"},
{"https://github.com/go-kratos/go-kratos.dev.git", "go-kratos", "go-kratos.dev"},
}
for _, url := range urls {
owner, repo := ParseGithubURL(url.url)
if owner != url.owner {
t.Fatalf("owner want: %s, got: %s", owner, url.owner)
}
if repo != url.repo {
t.Fatalf("repo want: %s, got: %s", repo, url.repo)
}
}
}

@ -1,67 +0,0 @@
package project
import (
"context"
"fmt"
"os"
"path/filepath"
"github.com/AlecAivazis/survey/v2"
"github.com/fatih/color"
"github.com/go-kratos/kratos/cmd/kratos/v2/internal/base"
)
var repoAddIgnores = []string{
".git", ".github", "api", "README.md", "LICENSE", "go.mod", "go.sum", "third_party", "openapi.yaml", ".gitignore",
}
func (p *Project) Add(ctx context.Context, dir string, layout string, branch string, mod string) error {
to := filepath.Join(dir, p.Name)
if _, err := os.Stat(to); !os.IsNotExist(err) {
fmt.Printf("🚫 %s already exists\n", p.Name)
override := false
prompt := &survey.Confirm{
Message: "📂 Do you want to override the folder ?",
Help: "Delete the existing folder and create the project.",
}
e := survey.AskOne(prompt, &override)
if e != nil {
return e
}
if !override {
return err
}
os.RemoveAll(to)
}
fmt.Printf("🚀 Add service %s, layout repo is %s, please wait a moment.\n\n", p.Name, layout)
repo := base.NewRepo(layout, branch)
if err := repo.CopyToV2(ctx, to, filepath.Join(mod, p.Path), repoAddIgnores, []string{filepath.Join(p.Path, "api"), "api"}); err != nil {
return err
}
e := os.Rename(
filepath.Join(to, "cmd", "server"),
filepath.Join(to, "cmd", p.Name),
)
if e != nil {
return e
}
base.Tree(to, dir)
fmt.Printf("\n🍺 Repository creation succeeded %s\n", color.GreenString(p.Name))
fmt.Print("💻 Use the following command to add a project 👇:\n\n")
fmt.Println(color.WhiteString("$ cd %s", p.Name))
fmt.Println(color.WhiteString("$ go generate ./..."))
fmt.Println(color.WhiteString("$ go build -o ./bin/ ./... "))
fmt.Println(color.WhiteString("$ ./bin/%s -conf ./configs\n", p.Name))
fmt.Println(" 🤝 Thanks for using Kratos")
fmt.Println(" 📚 Tutorial: https://go-kratos.dev/docs/getting-started/start")
return nil
}

@ -1,64 +0,0 @@
package project
import (
"context"
"fmt"
"os"
"path/filepath"
"github.com/AlecAivazis/survey/v2"
"github.com/fatih/color"
"github.com/go-kratos/kratos/cmd/kratos/v2/internal/base"
)
// Project is a project template.
type Project struct {
Name string
Path string
}
// New new a project from remote repo.
func (p *Project) New(ctx context.Context, dir string, layout string, branch string) error {
to := filepath.Join(dir, p.Name)
if _, err := os.Stat(to); !os.IsNotExist(err) {
fmt.Printf("🚫 %s already exists\n", p.Name)
prompt := &survey.Confirm{
Message: "📂 Do you want to override the folder ?",
Help: "Delete the existing folder and create the project.",
}
var override bool
e := survey.AskOne(prompt, &override)
if e != nil {
return e
}
if !override {
return err
}
os.RemoveAll(to)
}
fmt.Printf("🚀 Creating service %s, layout repo is %s, please wait a moment.\n\n", p.Name, layout)
repo := base.NewRepo(layout, branch)
if err := repo.CopyTo(ctx, to, p.Name, []string{".git", ".github"}); err != nil {
return err
}
e := os.Rename(
filepath.Join(to, "cmd", "server"),
filepath.Join(to, "cmd", p.Name),
)
if e != nil {
return e
}
base.Tree(to, dir)
fmt.Printf("\n🍺 Project creation succeeded %s\n", color.GreenString(p.Name))
fmt.Print("💻 Use the following command to start the project 👇:\n\n")
fmt.Println(color.WhiteString("$ cd %s", p.Name))
fmt.Println(color.WhiteString("$ go generate ./..."))
fmt.Println(color.WhiteString("$ go build -o ./bin/ ./... "))
fmt.Println(color.WhiteString("$ ./bin/%s -conf ./configs\n", p.Name))
fmt.Println(" 🤝 Thanks for using Kratos")
fmt.Println(" 📚 Tutorial: https://go-kratos.dev/docs/getting-started/start")
return nil
}

@ -1,149 +0,0 @@
package project
import (
"context"
"errors"
"fmt"
"os"
"path/filepath"
"strings"
"time"
"github.com/AlecAivazis/survey/v2"
"github.com/spf13/cobra"
"github.com/go-kratos/kratos/cmd/kratos/v2/internal/base"
)
// CmdNew represents the new command.
var CmdNew = &cobra.Command{
Use: "new",
Short: "Create a service template",
Long: "Create a service project using the repository template. Example: kratos new helloworld",
Run: run,
}
var (
repoURL string
branch string
timeout string
nomod bool
)
func init() {
if repoURL = os.Getenv("KRATOS_LAYOUT_REPO"); repoURL == "" {
repoURL = "https://github.com/go-kratos/kratos-layout.git"
}
timeout = "60s"
CmdNew.Flags().StringVarP(&repoURL, "repo-url", "r", repoURL, "layout repo")
CmdNew.Flags().StringVarP(&branch, "branch", "b", branch, "repo branch")
CmdNew.Flags().StringVarP(&timeout, "timeout", "t", timeout, "time out")
CmdNew.Flags().BoolVarP(&nomod, "nomod", "", nomod, "retain go mod")
}
func run(_ *cobra.Command, args []string) {
wd, err := os.Getwd()
if err != nil {
panic(err)
}
t, err := time.ParseDuration(timeout)
if err != nil {
panic(err)
}
ctx, cancel := context.WithTimeout(context.Background(), t)
defer cancel()
name := ""
if len(args) == 0 {
prompt := &survey.Input{
Message: "What is project name ?",
Help: "Created project name.",
}
err = survey.AskOne(prompt, &name)
if err != nil || name == "" {
return
}
} else {
name = args[0]
}
projectName, workingDir := processProjectParams(name, wd)
p := &Project{Name: projectName}
done := make(chan error, 1)
go func() {
if !nomod {
done <- p.New(ctx, workingDir, repoURL, branch)
return
}
projectRoot := getgomodProjectRoot(workingDir)
if gomodIsNotExistIn(projectRoot) {
done <- fmt.Errorf("🚫 go.mod don't exists in %s", projectRoot)
return
}
p.Path, err = filepath.Rel(projectRoot, filepath.Join(workingDir, projectName))
if err != nil {
done <- fmt.Errorf("🚫 failed to get relative path: %v", err)
return
}
mod, e := base.ModulePath(filepath.Join(projectRoot, "go.mod"))
if e != nil {
done <- fmt.Errorf("🚫 failed to parse `go.mod`: %v", e)
return
}
// Get the relative path for adding a project based on Go modules
p.Path = filepath.Join(strings.TrimPrefix(workingDir, projectRoot+"/"), p.Name)
done <- p.Add(ctx, workingDir, repoURL, branch, mod)
}()
select {
case <-ctx.Done():
if errors.Is(ctx.Err(), context.DeadlineExceeded) {
fmt.Fprint(os.Stderr, "\033[31mERROR: project creation timed out\033[m\n")
return
}
fmt.Fprintf(os.Stderr, "\033[31mERROR: failed to create project(%s)\033[m\n", ctx.Err().Error())
case err = <-done:
if err != nil {
fmt.Fprintf(os.Stderr, "\033[31mERROR: Failed to create project(%s)\033[m\n", err.Error())
}
}
}
func processProjectParams(projectName string, workingDir string) (projectNameResult, workingDirResult string) {
_projectDir := projectName
_workingDir := workingDir
// Process ProjectName with system variable
if strings.HasPrefix(projectName, "~") {
homeDir, err := os.UserHomeDir()
if err != nil {
// cannot get user home return fallback place dir
return _projectDir, _workingDir
}
_projectDir = filepath.Join(homeDir, projectName[2:])
}
// check path is relative
if !filepath.IsAbs(projectName) {
absPath, err := filepath.Abs(projectName)
if err != nil {
return _projectDir, _workingDir
}
_projectDir = absPath
}
return filepath.Base(_projectDir), filepath.Dir(_projectDir)
}
func getgomodProjectRoot(dir string) string {
if dir == filepath.Dir(dir) {
return dir
}
if gomodIsNotExistIn(dir) {
return getgomodProjectRoot(filepath.Dir(dir))
}
return dir
}
func gomodIsNotExistIn(dir string) bool {
_, e := os.Stat(filepath.Join(dir, "go.mod"))
return os.IsNotExist(e)
}

@ -1,29 +0,0 @@
//go:build linux
// +build linux
package project
import (
"testing"
)
func Test_processProjectParams(t *testing.T) {
type args struct {
projectName string
fallbackPlaceDir string
}
tests := []struct {
name string
args args
want string
}{
{"absLinux", args{projectName: "/home/kratos/awesome/go/demo", fallbackPlaceDir: ""}, "/home/kratos/awesome/go"},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if _, got := processProjectParams(tt.args.projectName, tt.args.fallbackPlaceDir); got != tt.want {
t.Errorf("processProjectParams() = %v, want %v", got, tt.want)
}
})
}
}

@ -1,144 +0,0 @@
package project
import (
"fmt"
"go/parser"
"go/token"
"os"
"path/filepath"
"testing"
"github.com/go-kratos/kratos/cmd/kratos/v2/internal/base"
)
// TestCmdNew tests the `kratos new` command.
func TestCmdNew(t *testing.T) {
cwd := changeCurrentDir(t)
projectName := "helloworld"
// create a new project
CmdNew.SetArgs([]string{projectName})
if err := CmdNew.Execute(); err != nil {
t.Fatalf("executing command: %v", err)
}
// check that the expected files were created
for _, file := range []string{
"go.mod",
"go.sum",
"README.md",
"cmd/helloworld/main.go",
} {
if _, err := os.Stat(filepath.Join(cwd, projectName, file)); err != nil {
t.Errorf("expected file %s to exist", file)
}
}
// check that the go.mod file contains the expected module name
assertGoMod(t, filepath.Join(cwd, projectName, "go.mod"), projectName)
assertImportsInclude(t, filepath.Join(cwd, projectName, "cmd", projectName, "wire.go"), fmt.Sprintf(`"%s/internal/biz"`, projectName))
}
// TestCmdNewNoMod tests the `kratos new` command with the --nomod flag.
func TestCmdNewNoMod(t *testing.T) {
cwd := changeCurrentDir(t)
// create a new project
CmdNew.SetArgs([]string{"project"})
if err := CmdNew.Execute(); err != nil {
t.Fatalf("executing command: %v", err)
}
// add new app with --nomod flag
CmdNew.SetArgs([]string{"--nomod", "project/app/user"})
if err := CmdNew.Execute(); err != nil {
t.Fatalf("executing command: %v", err)
}
// check that the expected files were created
for _, file := range []string{
"go.mod",
"go.sum",
"README.md",
"cmd/project/main.go",
"app/user/cmd/user/main.go",
} {
if _, err := os.Stat(filepath.Join(cwd, "project", file)); err != nil {
t.Errorf("expected file %s to exist", file)
}
}
assertImportsInclude(t, filepath.Join(cwd, "project/app/user/cmd/user/wire.go"), `"project/app/user/internal/biz"`)
}
// assertImportsInclude checks that the file at path contains the expected import.
func assertImportsInclude(t *testing.T, path, expected string) {
t.Helper()
got, err := imports(path)
if err != nil {
t.Fatalf("getting imports: %v", err)
}
for _, imp := range got {
if imp == expected {
return
}
}
t.Errorf("expected imports to include %s, got %v", expected, got)
}
// imports returns the imports in the file at path.
func imports(path string) ([]string, error) {
fset := token.NewFileSet()
f, err := parser.ParseFile(fset, path, nil, parser.ImportsOnly)
if err != nil {
return nil, err
}
imports := make([]string, 0, len(f.Imports))
for _, s := range f.Imports {
imports = append(imports, s.Path.Value)
}
return imports, nil
}
// assertGoMod checks that the go.mod file contains the expected module name.
func assertGoMod(t *testing.T, path, expected string) {
t.Helper()
got, err := base.ModulePath(path)
if err != nil {
t.Fatalf("getting module path: %v", err)
}
if got != expected {
t.Errorf("expected module name %s, got %s", expected, got)
}
}
// change the working directory to the tempdir
func changeCurrentDir(t *testing.T) string {
t.Helper()
tmp := t.TempDir()
oldCWD, err := os.Getwd()
if err != nil {
t.Fatalf("getting working directory: %v", err)
}
if err := os.Chdir(tmp); err != nil {
t.Fatalf("changing working directory: %v", err)
}
t.Cleanup(func() {
if err := os.Chdir(oldCWD); err != nil {
t.Fatalf("restoring working directory: %v", err)
}
})
return tmp
}

@ -1,30 +0,0 @@
//go:build windows
// +build windows
package project
import (
"testing"
)
func Test_processProjectParams(t *testing.T) {
type args struct {
projectName string
fallbackPlaceDir string
}
tests := []struct {
name string
args args
want string
}{
{"absWindows", args{projectName: "c:\\kratos\\awesome\\go\\demo", fallbackPlaceDir: ""}, "c:\\kratos\\awesome\\go"},
//{"relativeWindows", args{projectName: "/home/kratos/awesome/go/demo", fallbackPlaceDir: ""}, "/home/kratos/awesome/go"},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if _, got := processProjectParams(tt.args.projectName, tt.args.fallbackPlaceDir); got != tt.want {
t.Errorf("getProjectPlaceDir() = %v, want %v", got, tt.want)
}
})
}
}

@ -1,78 +0,0 @@
package add
import (
"fmt"
"os"
"strings"
"github.com/spf13/cobra"
"golang.org/x/mod/modfile"
"golang.org/x/text/cases"
"golang.org/x/text/language"
)
// CmdAdd represents the add command.
var CmdAdd = &cobra.Command{
Use: "add",
Short: "Add a proto API template",
Long: "Add a proto API template. Example: kratos proto add helloworld/v1/hello.proto",
Run: run,
}
func run(_ *cobra.Command, args []string) {
if len(args) == 0 {
fmt.Println("Please enter the proto file or directory")
return
}
input := args[0]
n := strings.LastIndex(input, "/")
if n == -1 {
fmt.Println("The proto path needs to be hierarchical.")
return
}
path := input[:n]
fileName := input[n+1:]
pkgName := strings.ReplaceAll(path, "/", ".")
p := &Proto{
Name: fileName,
Path: path,
Package: pkgName,
GoPackage: goPackage(path),
JavaPackage: javaPackage(pkgName),
Service: serviceName(fileName),
}
if err := p.Generate(); err != nil {
fmt.Println(err)
return
}
}
func modName() string {
modBytes, err := os.ReadFile("go.mod")
if err != nil {
if modBytes, err = os.ReadFile("../go.mod"); err != nil {
return ""
}
}
return modfile.ModulePath(modBytes)
}
func goPackage(path string) string {
s := strings.Split(path, "/")
return modName() + "/" + path + ";" + s[len(s)-1]
}
func javaPackage(name string) string {
return name
}
func serviceName(name string) string {
return toUpperCamelCase(strings.Split(name, ".")[0])
}
func toUpperCamelCase(s string) string {
s = strings.ReplaceAll(s, "_", " ")
s = cases.Title(language.Und, cases.NoLower).String(s)
return strings.ReplaceAll(s, " ", "")
}

@ -1,38 +0,0 @@
package add
import "testing"
func TestUnderscoreToUpperCamelCase(t *testing.T) {
tests := []struct {
name string
want string
}{
{
name: "hello_world",
want: "HelloWorld",
},
{
name: "v2_kratos_dev",
want: "V2KratosDev",
},
{
name: "www_Google_com",
want: "WwwGoogleCom",
},
{
name: "wwwBaidu_com",
want: "WwwBaiduCom",
},
{
name: "HelloWorld",
want: "HelloWorld",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := toUpperCamelCase(tt.name); got != tt.want {
t.Errorf("toUpperCamelCase() = %v, want %v", got, tt.want)
}
})
}
}

@ -1,40 +0,0 @@
package add
import (
"fmt"
"os"
"path/filepath"
)
// Proto is a proto generator.
type Proto struct {
Name string
Path string
Service string
Package string
GoPackage string
JavaPackage string
}
// Generate generate a proto template.
func (p *Proto) Generate() error {
body, err := p.execute()
if err != nil {
return err
}
wd, err := os.Getwd()
if err != nil {
panic(err)
}
to := filepath.Join(wd, p.Path)
if _, err := os.Stat(to); os.IsNotExist(err) {
if err := os.MkdirAll(to, 0o700); err != nil {
return err
}
}
name := filepath.Join(to, p.Name)
if _, err := os.Stat(name); !os.IsNotExist(err) {
return fmt.Errorf("%s already exists", p.Name)
}
return os.WriteFile(name, body, 0o644)
}

@ -1,52 +0,0 @@
package add
import (
"bytes"
"strings"
"text/template"
)
const protoTemplate = `
syntax = "proto3";
package {{.Package}};
option go_package = "{{.GoPackage}}";
option java_multiple_files = true;
option java_package = "{{.JavaPackage}}";
service {{.Service}} {
rpc Create{{.Service}} (Create{{.Service}}Request) returns (Create{{.Service}}Reply);
rpc Update{{.Service}} (Update{{.Service}}Request) returns (Update{{.Service}}Reply);
rpc Delete{{.Service}} (Delete{{.Service}}Request) returns (Delete{{.Service}}Reply);
rpc Get{{.Service}} (Get{{.Service}}Request) returns (Get{{.Service}}Reply);
rpc List{{.Service}} (List{{.Service}}Request) returns (List{{.Service}}Reply);
}
message Create{{.Service}}Request {}
message Create{{.Service}}Reply {}
message Update{{.Service}}Request {}
message Update{{.Service}}Reply {}
message Delete{{.Service}}Request {}
message Delete{{.Service}}Reply {}
message Get{{.Service}}Request {}
message Get{{.Service}}Reply {}
message List{{.Service}}Request {}
message List{{.Service}}Reply {}
`
func (p *Proto) execute() ([]byte, error) {
buf := new(bytes.Buffer)
tmpl, err := template.New("proto").Parse(strings.TrimSpace(protoTemplate))
if err != nil {
return nil, err
}
if err := tmpl.Execute(buf, p); err != nil {
return nil, err
}
return buf.Bytes(), nil
}

@ -1,130 +0,0 @@
package client
import (
"fmt"
"os"
"os/exec"
"path/filepath"
"regexp"
"strings"
"github.com/spf13/cobra"
"github.com/go-kratos/kratos/cmd/kratos/v2/internal/base"
)
// CmdClient represents the source command.
var CmdClient = &cobra.Command{
Use: "client",
Short: "Generate the proto client code",
Long: "Generate the proto client code. Example: kratos proto client helloworld.proto",
Run: run,
}
var protoPath string
func init() {
if protoPath = os.Getenv("KRATOS_PROTO_PATH"); protoPath == "" {
protoPath = "./third_party"
}
CmdClient.Flags().StringVarP(&protoPath, "proto_path", "p", protoPath, "proto path")
}
func run(_ *cobra.Command, args []string) {
if len(args) == 0 {
fmt.Println("Please enter the proto file or directory")
return
}
var (
err error
proto = strings.TrimSpace(args[0])
)
if err = look("protoc-gen-go", "protoc-gen-go-grpc", "protoc-gen-go-http", "protoc-gen-go-errors", "protoc-gen-openapi"); err != nil {
// update the kratos plugins
cmd := exec.Command("kratos", "upgrade")
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err = cmd.Run(); err != nil {
fmt.Println(err)
return
}
}
if strings.HasSuffix(proto, ".proto") {
err = generate(proto, args)
} else {
err = walk(proto, args)
}
if err != nil {
fmt.Println(err)
}
}
func look(name ...string) error {
for _, n := range name {
if _, err := exec.LookPath(n); err != nil {
return err
}
}
return nil
}
func walk(dir string, args []string) error {
if dir == "" {
dir = "."
}
return filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
if ext := filepath.Ext(path); ext != ".proto" || strings.HasPrefix(path, "third_party") {
return nil
}
return generate(path, args)
})
}
// generate is used to execute the generate command for the specified proto file
func generate(proto string, args []string) error {
input := []string{
"--proto_path=.",
}
if pathExists(protoPath) {
input = append(input, "--proto_path="+protoPath)
}
inputExt := []string{
"--proto_path=" + base.KratosMod(),
"--proto_path=" + filepath.Join(base.KratosMod(), "third_party"),
"--go_out=paths=source_relative:.",
"--go-grpc_out=paths=source_relative:.",
"--go-http_out=paths=source_relative:.",
"--go-errors_out=paths=source_relative:.",
"--openapi_out=paths=source_relative:.",
}
input = append(input, inputExt...)
protoBytes, err := os.ReadFile(proto)
if err == nil && len(protoBytes) > 0 {
if ok, _ := regexp.Match(`\n[^/]*(import)\s+"validate/validate.proto"`, protoBytes); ok {
input = append(input, "--validate_out=lang=go,paths=source_relative:.")
}
}
input = append(input, proto)
for _, a := range args {
if strings.HasPrefix(a, "-") {
input = append(input, a)
}
}
fd := exec.Command("protoc", input...)
fd.Stdout = os.Stdout
fd.Stderr = os.Stderr
fd.Dir = "."
if err := fd.Run(); err != nil {
return err
}
fmt.Printf("proto: %s\n", proto)
return nil
}
func pathExists(path string) bool {
_, err := os.Stat(path)
if err != nil {
return os.IsExist(err)
}
return true
}

@ -1,22 +0,0 @@
package proto
import (
"github.com/spf13/cobra"
"github.com/go-kratos/kratos/cmd/kratos/v2/internal/proto/add"
"github.com/go-kratos/kratos/cmd/kratos/v2/internal/proto/client"
"github.com/go-kratos/kratos/cmd/kratos/v2/internal/proto/server"
)
// CmdProto represents the proto command.
var CmdProto = &cobra.Command{
Use: "proto",
Short: "Generate the proto files",
Long: "Generate the proto files.",
}
func init() {
CmdProto.AddCommand(add.CmdAdd)
CmdProto.AddCommand(client.CmdClient)
CmdProto.AddCommand(server.CmdServer)
}

@ -1,120 +0,0 @@
package server
import (
"fmt"
"log"
"os"
"path/filepath"
"strings"
"github.com/emicklei/proto"
"github.com/spf13/cobra"
"golang.org/x/text/cases"
"golang.org/x/text/language"
)
// CmdServer the service command.
var CmdServer = &cobra.Command{
Use: "server",
Short: "Generate the proto server implementations",
Long: "Generate the proto server implementations. Example: kratos proto server api/xxx.proto --target-dir=internal/service",
Run: run,
}
var targetDir string
func init() {
CmdServer.Flags().StringVarP(&targetDir, "target-dir", "t", "internal/service", "generate target directory")
}
func run(_ *cobra.Command, args []string) {
if len(args) == 0 {
fmt.Fprintln(os.Stderr, "Please specify the proto file. Example: kratos proto server api/xxx.proto")
return
}
reader, err := os.Open(args[0])
if err != nil {
log.Fatal(err)
}
defer reader.Close()
parser := proto.NewParser(reader)
definition, err := parser.Parse()
if err != nil {
log.Fatal(err)
}
var (
pkg string
res []*Service
)
proto.Walk(definition,
proto.WithOption(func(o *proto.Option) {
if o.Name == "go_package" {
pkg = strings.Split(o.Constant.Source, ";")[0]
}
}),
proto.WithService(func(s *proto.Service) {
cs := &Service{
Package: pkg,
Service: serviceName(s.Name),
}
for _, e := range s.Elements {
r, ok := e.(*proto.RPC)
if !ok {
continue
}
cs.Methods = append(cs.Methods, &Method{
Service: serviceName(s.Name), Name: serviceName(r.Name), Request: parametersName(r.RequestType),
Reply: parametersName(r.ReturnsType), Type: getMethodType(r.StreamsRequest, r.StreamsReturns),
})
}
res = append(res, cs)
}),
)
if _, err := os.Stat(targetDir); os.IsNotExist(err) {
fmt.Printf("Target directory: %s does not exsit\n", targetDir)
return
}
for _, s := range res {
to := filepath.Join(targetDir, strings.ToLower(s.Service)+".go")
if _, err := os.Stat(to); !os.IsNotExist(err) {
fmt.Fprintf(os.Stderr, "%s already exists: %s\n", s.Service, to)
continue
}
b, err := s.execute()
if err != nil {
log.Fatal(err)
}
if err := os.WriteFile(to, b, 0o644); err != nil {
log.Fatal(err)
}
fmt.Println(to)
}
}
func getMethodType(streamsRequest, streamsReturns bool) MethodType {
if !streamsRequest && !streamsReturns {
return unaryType
} else if streamsRequest && streamsReturns {
return twoWayStreamsType
} else if streamsRequest {
return requestStreamsType
} else if streamsReturns {
return returnsStreamsType
}
return unaryType
}
func parametersName(name string) string {
return strings.ReplaceAll(name, ".", "_")
}
func serviceName(name string) string {
return toUpperCamelCase(strings.Split(name, ".")[0])
}
func toUpperCamelCase(s string) string {
s = strings.ReplaceAll(s, "_", " ")
s = cases.Title(language.Und, cases.NoLower).String(s)
return strings.ReplaceAll(s, " ", "")
}

@ -1,102 +0,0 @@
package server
import "testing"
func Test_serviceName(t *testing.T) {
type args struct {
str string
}
tests := []struct {
name string
args args
want string
}{
{
name: "serviceName on lowercase words",
args: args{str: "helloworld"},
want: "Helloworld",
},
{
name: "serviceName on uppercase words",
args: args{str: "HELLOWORLD"},
want: "HELLOWORLD",
},
{
name: "serviceName on lowercase words with spaces",
args: args{str: "hello world"},
want: "HelloWorld",
},
{
name: "serviceName on uppercase words with spaces",
args: args{str: "HELLO WORLD"},
want: "HELLOWORLD",
},
{
name: "serviceName on Lower Camel Case words",
args: args{str: "helloWorld"},
want: "HelloWorld",
},
{
name: "serviceName on Lower Camel Case words",
args: args{str: "helloWorld"},
want: "HelloWorld",
},
{
name: "serviceName on Upper Camel Case words",
args: args{str: "HelloWorld"},
want: "HelloWorld",
},
{
name: "serviceName on Upper Camel Case words",
args: args{str: "hello_world"},
want: "HelloWorld",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := serviceName(tt.args.str); got != tt.want {
t.Errorf("serviceName() = %v, want %v", got, tt.want)
}
})
}
}
func Test_parametersName(t *testing.T) {
type args struct {
name string
}
tests := []struct {
name string
args args
want string
}{
{
name: "parametersName on not nested",
args: args{
name: "MessageResponse",
},
want: "MessageResponse",
},
{
name: "parametersName on One layer of nesting",
args: args{
name: "Message.Response",
},
want: "Message_Response",
},
{
name: "parametersName on Two layer of nesting",
args: args{
name: "Message.Message2.Response",
},
want: "Message_Message2_Response",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := parametersName(tt.args.name); got != tt.want {
t.Errorf("parametersName() = %v, want %v", got, tt.want)
}
})
}
}

@ -1,142 +0,0 @@
package server
import (
"bytes"
"html/template"
)
//nolint:lll
var serviceTemplate = `
{{- /* delete empty line */ -}}
package service
import (
{{- if .UseContext }}
"context"
{{- end }}
{{- if .UseIO }}
"io"
{{- end }}
pb "{{ .Package }}"
{{- if .GoogleEmpty }}
"google.golang.org/protobuf/types/known/emptypb"
{{- end }}
)
type {{ .Service }}Service struct {
pb.Unimplemented{{ .Service }}Server
}
func New{{ .Service }}Service() *{{ .Service }}Service {
return &{{ .Service }}Service{}
}
{{- $s1 := "google.protobuf.Empty" }}
{{ range .Methods }}
{{- if eq .Type 1 }}
func (s *{{ .Service }}Service) {{ .Name }}(ctx context.Context, req {{ if eq .Request $s1 }}*emptypb.Empty{{ else }}*pb.{{ .Request }}{{ end }}) ({{ if eq .Reply $s1 }}*emptypb.Empty{{ else }}*pb.{{ .Reply }}{{ end }}, error) {
return {{ if eq .Reply $s1 }}&emptypb.Empty{}{{ else }}&pb.{{ .Reply }}{}{{ end }}, nil
}
{{- else if eq .Type 2 }}
func (s *{{ .Service }}Service) {{ .Name }}(conn pb.{{ .Service }}_{{ .Name }}Server) error {
for {
req, err := conn.Recv()
if err == io.EOF {
return nil
}
if err != nil {
return err
}
err = conn.Send(&pb.{{ .Reply }}{})
if err != nil {
return err
}
}
}
{{- else if eq .Type 3 }}
func (s *{{ .Service }}Service) {{ .Name }}(conn pb.{{ .Service }}_{{ .Name }}Server) error {
for {
req, err := conn.Recv()
if err == io.EOF {
return conn.SendAndClose(&pb.{{ .Reply }}{})
}
if err != nil {
return err
}
}
}
{{- else if eq .Type 4 }}
func (s *{{ .Service }}Service) {{ .Name }}(req {{ if eq .Request $s1 }}*emptypb.Empty
{{ else }}*pb.{{ .Request }}{{ end }}, conn pb.{{ .Service }}_{{ .Name }}Server) error {
for {
err := conn.Send(&pb.{{ .Reply }}{})
if err != nil {
return err
}
}
}
{{- end }}
{{- end }}
`
type MethodType uint8
const (
unaryType MethodType = 1
twoWayStreamsType MethodType = 2
requestStreamsType MethodType = 3
returnsStreamsType MethodType = 4
)
// Service is a proto service.
type Service struct {
Package string
Service string
Methods []*Method
GoogleEmpty bool
UseIO bool
UseContext bool
}
// Method is a proto method.
type Method struct {
Service string
Name string
Request string
Reply string
// type: unary or stream
Type MethodType
}
func (s *Service) execute() ([]byte, error) {
const empty = "google.protobuf.Empty"
buf := new(bytes.Buffer)
for _, method := range s.Methods {
if (method.Type == unaryType && (method.Request == empty || method.Reply == empty)) ||
(method.Type == returnsStreamsType && method.Request == empty) {
s.GoogleEmpty = true
}
if method.Type == twoWayStreamsType || method.Type == requestStreamsType {
s.UseIO = true
}
if method.Type == unaryType {
s.UseContext = true
}
}
tmpl, err := template.New("service").Parse(serviceTemplate)
if err != nil {
return nil, err
}
if err := tmpl.Execute(buf, s); err != nil {
return nil, err
}
return buf.Bytes(), nil
}

@ -1,145 +0,0 @@
package run
import (
"fmt"
"os"
"os/exec"
"path/filepath"
"strings"
"github.com/AlecAivazis/survey/v2"
"github.com/spf13/cobra"
)
// CmdRun run project command.
var CmdRun = &cobra.Command{
Use: "run",
Short: "Run project",
Long: "Run project. Example: kratos run",
Run: Run,
}
var targetDir string
func init() {
CmdRun.Flags().StringVarP(&targetDir, "work", "w", "", "target working directory")
}
// Run run project.
func Run(cmd *cobra.Command, args []string) {
var dir string
cmdArgs, programArgs := splitArgs(cmd, args)
if len(cmdArgs) > 0 {
dir = cmdArgs[0]
}
base, err := os.Getwd()
if err != nil {
fmt.Fprintf(os.Stderr, "\033[31mERROR: %s\033[m\n", err)
return
}
if dir == "" {
// find the directory containing the cmd/*
cmdPath, err := findCMD(base)
if err != nil {
fmt.Fprintf(os.Stderr, "\033[31mERROR: %s\033[m\n", err)
return
}
switch len(cmdPath) {
case 0:
fmt.Fprintf(os.Stderr, "\033[31mERROR: %s\033[m\n", "The cmd directory cannot be found in the current directory")
return
case 1:
for _, v := range cmdPath {
dir = v
}
default:
var cmdPaths []string
for k := range cmdPath {
cmdPaths = append(cmdPaths, k)
}
prompt := &survey.Select{
Message: "Which directory do you want to run?",
Options: cmdPaths,
PageSize: 10,
}
e := survey.AskOne(prompt, &dir)
if e != nil || dir == "" {
return
}
dir = cmdPath[dir]
}
}
fd := exec.Command("go", append([]string{"run", dir}, programArgs...)...)
fd.Stdout = os.Stdout
fd.Stderr = os.Stderr
fd.Dir = dir
changeWorkingDirectory(fd, targetDir)
if err := fd.Run(); err != nil {
fmt.Fprintf(os.Stderr, "\033[31mERROR: %s\033[m\n", err.Error())
return
}
}
func splitArgs(cmd *cobra.Command, args []string) (cmdArgs, programArgs []string) {
dashAt := cmd.ArgsLenAtDash()
if dashAt >= 0 {
return args[:dashAt], args[dashAt:]
}
return args, []string{}
}
func findCMD(base string) (map[string]string, error) {
wd, err := os.Getwd()
if err != nil {
return nil, err
}
if !strings.HasSuffix(wd, "/") {
wd += "/"
}
var root bool
next := func(dir string) (map[string]string, error) {
cmdPath := make(map[string]string)
err := filepath.Walk(dir, func(walkPath string, info os.FileInfo, err error) error {
// multi level directory is not allowed under the cmdPath directory, so it is judged that the path ends with cmdPath.
if strings.HasSuffix(walkPath, "cmd") {
paths, err := os.ReadDir(walkPath)
if err != nil {
return err
}
for _, fileInfo := range paths {
if fileInfo.IsDir() {
abs := filepath.Join(walkPath, fileInfo.Name())
cmdPath[strings.TrimPrefix(abs, wd)] = abs
}
}
return nil
}
if info.Name() == "go.mod" {
root = true
}
return nil
})
return cmdPath, err
}
for i := 0; i < 5; i++ {
tmp := base
cmd, err := next(tmp)
if err != nil {
return nil, err
}
if len(cmd) > 0 {
return cmd, nil
}
if root {
break
}
_ = filepath.Join(base, "..")
}
return map[string]string{"": base}, nil
}
func changeWorkingDirectory(cmd *exec.Cmd, targetDir string) {
targetDir = strings.TrimSpace(targetDir)
if targetDir != "" {
cmd.Dir = targetDir
}
}

@ -1,32 +0,0 @@
package upgrade
import (
"fmt"
"github.com/spf13/cobra"
"github.com/go-kratos/kratos/cmd/kratos/v2/internal/base"
)
// CmdUpgrade represents the upgrade command.
var CmdUpgrade = &cobra.Command{
Use: "upgrade",
Short: "Upgrade the kratos tools",
Long: "Upgrade the kratos tools. Example: kratos upgrade",
Run: Run,
}
// Run upgrade the kratos tools.
func Run(_ *cobra.Command, _ []string) {
err := base.GoInstall(
"github.com/go-kratos/kratos/cmd/kratos/v2@latest",
"github.com/go-kratos/kratos/cmd/protoc-gen-go-http/v2@latest",
"github.com/go-kratos/kratos/cmd/protoc-gen-go-errors/v2@latest",
"google.golang.org/protobuf/cmd/protoc-gen-go@latest",
"google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest",
"github.com/google/gnostic/cmd/protoc-gen-openapi@latest",
)
if err != nil {
fmt.Println(err)
}
}

@ -1,34 +0,0 @@
package main
import (
"log"
"github.com/spf13/cobra"
"github.com/go-kratos/kratos/cmd/kratos/v2/internal/change"
"github.com/go-kratos/kratos/cmd/kratos/v2/internal/project"
"github.com/go-kratos/kratos/cmd/kratos/v2/internal/proto"
"github.com/go-kratos/kratos/cmd/kratos/v2/internal/run"
"github.com/go-kratos/kratos/cmd/kratos/v2/internal/upgrade"
)
var rootCmd = &cobra.Command{
Use: "kratos",
Short: "Kratos: An elegant toolkit for Go microservices.",
Long: `Kratos: An elegant toolkit for Go microservices.`,
Version: release,
}
func init() {
rootCmd.AddCommand(project.CmdNew)
rootCmd.AddCommand(proto.CmdProto)
rootCmd.AddCommand(upgrade.CmdUpgrade)
rootCmd.AddCommand(change.CmdChange)
rootCmd.AddCommand(run.CmdRun)
}
func main() {
if err := rootCmd.Execute(); err != nil {
log.Fatal(err)
}
}

@ -1,4 +0,0 @@
package main
// release is the current kratos tool version.
const release = "v2.6.3"

@ -1,135 +0,0 @@
package main
import (
"fmt"
"strings"
"unicode"
"golang.org/x/text/cases"
"golang.org/x/text/language"
"google.golang.org/protobuf/compiler/protogen"
"google.golang.org/protobuf/proto"
"github.com/go-kratos/kratos/cmd/protoc-gen-go-errors/v2/errors"
)
const (
errorsPackage = protogen.GoImportPath("github.com/go-kratos/kratos/v2/errors")
fmtPackage = protogen.GoImportPath("fmt")
)
var enCases = cases.Title(language.AmericanEnglish, cases.NoLower)
// generateFile generates a _errors.pb.go file containing kratos errors definitions.
func generateFile(gen *protogen.Plugin, file *protogen.File) *protogen.GeneratedFile {
if len(file.Enums) == 0 {
return nil
}
filename := file.GeneratedFilenamePrefix + "_errors.pb.go"
g := gen.NewGeneratedFile(filename, file.GoImportPath)
g.P("// Code generated by protoc-gen-go-errors. DO NOT EDIT.")
g.P()
g.P("package ", file.GoPackageName)
g.P()
g.QualifiedGoIdent(fmtPackage.Ident(""))
generateFileContent(gen, file, g)
return g
}
// generateFileContent generates the kratos errors definitions, excluding the package statement.
func generateFileContent(gen *protogen.Plugin, file *protogen.File, g *protogen.GeneratedFile) {
if len(file.Enums) == 0 {
return
}
g.P("// This is a compile-time assertion to ensure that this generated file")
g.P("// is compatible with the kratos package it is being compiled against.")
g.P("const _ = ", errorsPackage.Ident("SupportPackageIsVersion1"))
g.P()
index := 0
for _, enum := range file.Enums {
if !genErrorsReason(gen, file, g, enum) {
index++
}
}
// If all enums do not contain 'errors.code', the current file is skipped
if index == 0 {
g.Skip()
}
}
func genErrorsReason(_ *protogen.Plugin, _ *protogen.File, g *protogen.GeneratedFile, enum *protogen.Enum) bool {
defaultCode := proto.GetExtension(enum.Desc.Options(), errors.E_DefaultCode)
code := 0
if ok := defaultCode.(int32); ok != 0 {
code = int(ok)
}
if code > 600 || code < 0 {
panic(fmt.Sprintf("Enum '%s' range must be greater than 0 and less than or equal to 600", string(enum.Desc.Name())))
}
var ew errorWrapper
for _, v := range enum.Values {
enumCode := code
eCode := proto.GetExtension(v.Desc.Options(), errors.E_Code)
if ok := eCode.(int32); ok != 0 {
enumCode = int(ok)
}
// If the current enumeration does not contain 'errors.code'
// or the code value exceeds the range, the current enum will be skipped
if enumCode > 600 || enumCode < 0 {
panic(fmt.Sprintf("Enum '%s' range must be greater than 0 and less than or equal to 600", string(v.Desc.Name())))
}
if enumCode == 0 {
continue
}
comment := v.Comments.Leading.String()
if comment == "" {
comment = v.Comments.Trailing.String()
}
err := &errorInfo{
Name: string(enum.Desc.Name()),
Value: string(v.Desc.Name()),
CamelValue: case2Camel(string(v.Desc.Name())),
HTTPCode: enumCode,
Comment: comment,
HasComment: len(comment) > 0,
}
ew.Errors = append(ew.Errors, err)
}
if len(ew.Errors) == 0 {
return true
}
g.P(ew.execute())
return false
}
func case2Camel(name string) string {
if !strings.Contains(name, "_") {
if name == strings.ToUpper(name) {
name = strings.ToLower(name)
}
return enCases.String(name)
}
strs := strings.Split(name, "_")
words := make([]string, 0, len(strs))
for _, w := range strs {
hasLower := false
for _, r := range w {
if unicode.IsLower(r) {
hasLower = true
break
}
}
if !hasLower {
w = strings.ToLower(w)
}
w = enCases.String(w)
words = append(words, w)
}
return strings.Join(words, "")
}

@ -1,229 +0,0 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.26.0
// protoc v3.15.7
// source: errors.proto
package errors
import (
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
descriptorpb "google.golang.org/protobuf/types/descriptorpb"
reflect "reflect"
sync "sync"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
type Error struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Code int32 `protobuf:"varint,1,opt,name=code,proto3" json:"code,omitempty"`
Reason string `protobuf:"bytes,2,opt,name=reason,proto3" json:"reason,omitempty"`
Message string `protobuf:"bytes,3,opt,name=message,proto3" json:"message,omitempty"`
Metadata map[string]string `protobuf:"bytes,4,rep,name=metadata,proto3" json:"metadata,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
}
func (x *Error) Reset() {
*x = Error{}
if protoimpl.UnsafeEnabled {
mi := &file_errors_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *Error) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*Error) ProtoMessage() {}
func (x *Error) ProtoReflect() protoreflect.Message {
mi := &file_errors_proto_msgTypes[0]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use Error.ProtoReflect.Descriptor instead.
func (*Error) Descriptor() ([]byte, []int) {
return file_errors_proto_rawDescGZIP(), []int{0}
}
func (x *Error) GetCode() int32 {
if x != nil {
return x.Code
}
return 0
}
func (x *Error) GetReason() string {
if x != nil {
return x.Reason
}
return ""
}
func (x *Error) GetMessage() string {
if x != nil {
return x.Message
}
return ""
}
func (x *Error) GetMetadata() map[string]string {
if x != nil {
return x.Metadata
}
return nil
}
var file_errors_proto_extTypes = []protoimpl.ExtensionInfo{
{
ExtendedType: (*descriptorpb.EnumOptions)(nil),
ExtensionType: (*int32)(nil),
Field: 1108,
Name: "errors.default_code",
Tag: "varint,1108,opt,name=default_code",
Filename: "errors.proto",
},
{
ExtendedType: (*descriptorpb.EnumValueOptions)(nil),
ExtensionType: (*int32)(nil),
Field: 1109,
Name: "errors.code",
Tag: "varint,1109,opt,name=code",
Filename: "errors.proto",
},
}
// Extension fields to descriptorpb.EnumOptions.
var (
// optional int32 default_code = 1108;
E_DefaultCode = &file_errors_proto_extTypes[0]
)
// Extension fields to descriptorpb.EnumValueOptions.
var (
// optional int32 code = 1109;
E_Code = &file_errors_proto_extTypes[1]
)
var File_errors_proto protoreflect.FileDescriptor
var file_errors_proto_rawDesc = []byte{
0x0a, 0x0c, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x06,
0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x1a, 0x20, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70,
0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74,
0x6f, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xc3, 0x01, 0x0a, 0x05, 0x45, 0x72, 0x72,
0x6f, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05,
0x52, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e,
0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x18,
0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52,
0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x37, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61,
0x64, 0x61, 0x74, 0x61, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x65, 0x72, 0x72,
0x6f, 0x72, 0x73, 0x2e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61,
0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74,
0x61, 0x1a, 0x3b, 0x0a, 0x0d, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74,
0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20,
0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x3a, 0x40,
0x0a, 0x0c, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x63, 0x6f, 0x64, 0x65, 0x12, 0x1c,
0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66,
0x2e, 0x45, 0x6e, 0x75, 0x6d, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0xd4, 0x08, 0x20,
0x01, 0x28, 0x05, 0x52, 0x0b, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x43, 0x6f, 0x64, 0x65,
0x3a, 0x36, 0x0a, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x12, 0x21, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c,
0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6e, 0x75, 0x6d, 0x56,
0x61, 0x6c, 0x75, 0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0xd5, 0x08, 0x20, 0x01,
0x28, 0x05, 0x52, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x42, 0x59, 0x0a, 0x18, 0x63, 0x6f, 0x6d, 0x2e,
0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x6b, 0x72, 0x61, 0x74, 0x6f, 0x73, 0x2e, 0x65, 0x72,
0x72, 0x6f, 0x72, 0x73, 0x50, 0x01, 0x5a, 0x2c, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63,
0x6f, 0x6d, 0x2f, 0x67, 0x6f, 0x2d, 0x6b, 0x72, 0x61, 0x74, 0x6f, 0x73, 0x2f, 0x6b, 0x72, 0x61,
0x74, 0x6f, 0x73, 0x2f, 0x76, 0x32, 0x2f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x3b, 0x65, 0x72,
0x72, 0x6f, 0x72, 0x73, 0xa2, 0x02, 0x0c, 0x4b, 0x72, 0x61, 0x74, 0x6f, 0x73, 0x45, 0x72, 0x72,
0x6f, 0x72, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
file_errors_proto_rawDescOnce sync.Once
file_errors_proto_rawDescData = file_errors_proto_rawDesc
)
func file_errors_proto_rawDescGZIP() []byte {
file_errors_proto_rawDescOnce.Do(func() {
file_errors_proto_rawDescData = protoimpl.X.CompressGZIP(file_errors_proto_rawDescData)
})
return file_errors_proto_rawDescData
}
var file_errors_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
var file_errors_proto_goTypes = []interface{}{
(*Error)(nil), // 0: errors.Error
nil, // 1: errors.Error.MetadataEntry
(*descriptorpb.EnumOptions)(nil), // 2: google.protobuf.EnumOptions
(*descriptorpb.EnumValueOptions)(nil), // 3: google.protobuf.EnumValueOptions
}
var file_errors_proto_depIdxs = []int32{
1, // 0: errors.Error.metadata:type_name -> errors.Error.MetadataEntry
2, // 1: errors.default_code:extendee -> google.protobuf.EnumOptions
3, // 2: errors.code:extendee -> google.protobuf.EnumValueOptions
3, // [3:3] is the sub-list for method output_type
3, // [3:3] is the sub-list for method input_type
3, // [3:3] is the sub-list for extension type_name
1, // [1:3] is the sub-list for extension extendee
0, // [0:1] is the sub-list for field type_name
}
func init() { file_errors_proto_init() }
func file_errors_proto_init() {
if File_errors_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
file_errors_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*Error); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_errors_proto_rawDesc,
NumEnums: 0,
NumMessages: 2,
NumExtensions: 2,
NumServices: 0,
},
GoTypes: file_errors_proto_goTypes,
DependencyIndexes: file_errors_proto_depIdxs,
MessageInfos: file_errors_proto_msgTypes,
ExtensionInfos: file_errors_proto_extTypes,
}.Build()
File_errors_proto = out.File
file_errors_proto_rawDesc = nil
file_errors_proto_goTypes = nil
file_errors_proto_depIdxs = nil
}

@ -1,25 +0,0 @@
syntax = "proto3";
package errors;
option go_package = "github.com/go-kratos/kratos/v2/errors;errors";
option java_multiple_files = true;
option java_package = "com.github.kratos.errors";
option objc_class_prefix = "KratosErrors";
import "google/protobuf/descriptor.proto";
message Error {
int32 code = 1;
string reason = 2;
string message = 3;
map<string, string> metadata = 4;
};
extend google.protobuf.EnumOptions {
int32 default_code = 1108;
}
extend google.protobuf.EnumValueOptions {
int32 code = 1109;
}

@ -1,17 +0,0 @@
{{ range .Errors }}
{{ if .HasComment }}{{ .Comment }}{{ end -}}
func Is{{.CamelValue}}(err error) bool {
if err == nil {
return false
}
e := errors.FromError(err)
return e.Reason == {{ .Name }}_{{ .Value }}.String() && e.Code == {{ .HTTPCode }}
}
{{ if .HasComment }}{{ .Comment }}{{ end -}}
func Error{{ .CamelValue }}(format string, args ...interface{}) *errors.Error {
return errors.New({{ .HTTPCode }}, {{ .Name }}_{{ .Value }}.String(), fmt.Sprintf(format, args...))
}
{{- end }}

@ -1,62 +0,0 @@
package main
import "testing"
func Test_case2Camel(t *testing.T) {
type args struct {
name string
}
tests := []struct {
name string
args args
want string
}{
{
name: "snake1",
args: args{"SYSTEM_ERROR"},
want: "SystemError",
},
{
name: "snake2",
args: args{"System_Error"},
want: "SystemError",
},
{
name: "snake3",
args: args{"system_error"},
want: "SystemError",
},
{
name: "snake4",
args: args{"System_error"},
want: "SystemError",
},
{
name: "upper1",
args: args{"UNKNOWN"},
want: "Unknown",
},
{
name: "camel1",
args: args{"SystemError"},
want: "SystemError",
},
{
name: "camel2",
args: args{"systemError"},
want: "SystemError",
},
{
name: "lower1",
args: args{"system"},
want: "System",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := case2Camel(tt.args.name); got != tt.want {
t.Errorf("case2Camel() = %v, want %v", got, tt.want)
}
})
}
}

@ -1,8 +0,0 @@
module github.com/go-kratos/kratos/cmd/protoc-gen-go-errors/v2
go 1.16
require (
golang.org/x/text v0.3.8
google.golang.org/protobuf v1.28.0
)

@ -1,33 +0,0 @@
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/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.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.3.8 h1:nAL+RVCQ9uMn3vJZbV+MRnydTJFPf8qqY42YiA6MrqY=
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw=
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=

@ -1,32 +0,0 @@
package main
import (
"flag"
"fmt"
"google.golang.org/protobuf/compiler/protogen"
"google.golang.org/protobuf/types/pluginpb"
)
var showVersion = flag.Bool("version", false, "print the version and exit")
func main() {
flag.Parse()
if *showVersion {
fmt.Printf("protoc-gen-go-errors %v\n", release)
return
}
var flags flag.FlagSet
protogen.Options{
ParamFunc: flags.Set,
}.Run(func(gen *protogen.Plugin) error {
gen.SupportedFeatures = uint64(pluginpb.CodeGeneratorResponse_FEATURE_PROTO3_OPTIONAL)
for _, f := range gen.Files {
if !f.Generate {
continue
}
generateFile(gen, f)
}
return nil
})
}

@ -1,35 +0,0 @@
package main
import (
"bytes"
_ "embed"
"text/template"
)
//go:embed errorsTemplate.tpl
var errorsTemplate string
type errorInfo struct {
Name string
Value string
HTTPCode int
CamelValue string
Comment string
HasComment bool
}
type errorWrapper struct {
Errors []*errorInfo
}
func (e *errorWrapper) execute() string {
buf := new(bytes.Buffer)
tmpl, err := template.New("errors").Parse(errorsTemplate)
if err != nil {
panic(err)
}
if err := tmpl.Execute(buf, e); err != nil {
panic(err)
}
return buf.String()
}

@ -1,4 +0,0 @@
package main
// release is the current protoc-gen-go-errors version.
const release = "v2.6.3"

@ -1,8 +0,0 @@
module github.com/go-kratos/kratos/cmd/protoc-gen-go-http/v2
go 1.16
require (
google.golang.org/genproto v0.0.0-20220519153652-3a47de7e79bd
google.golang.org/protobuf v1.28.0
)

@ -1,130 +0,0 @@
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=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
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/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
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/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
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/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v1.2.0/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.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/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
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.5.0/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 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
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-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
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-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-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-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
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-20190423024810-112230192c58/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-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/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-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
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-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
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/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
google.golang.org/genproto v0.0.0-20220519153652-3a47de7e79bd h1:e0TwkXOdbnH/1x5rc5MZ/VYyiZ4v+RdVfrGMqEwT68I=
google.golang.org/genproto v0.0.0-20220519153652-3a47de7e79bd/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
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.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
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.46.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.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.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw=
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
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.v3 v3.0.0-20200313102051-9f266ea9e77c/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-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=

@ -1,334 +0,0 @@
package main
import (
"fmt"
"net/http"
"os"
"regexp"
"strings"
"google.golang.org/protobuf/reflect/protoreflect"
"google.golang.org/genproto/googleapis/api/annotations"
"google.golang.org/protobuf/compiler/protogen"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/types/descriptorpb"
)
const (
contextPackage = protogen.GoImportPath("context")
transportHTTPPackage = protogen.GoImportPath("github.com/go-kratos/kratos/v2/transport/http")
bindingPackage = protogen.GoImportPath("github.com/go-kratos/kratos/v2/transport/http/binding")
)
var methodSets = make(map[string]int)
// generateFile generates a _http.pb.go file containing kratos errors definitions.
func generateFile(gen *protogen.Plugin, file *protogen.File, omitempty bool, omitemptyPrefix string) *protogen.GeneratedFile {
if len(file.Services) == 0 || (omitempty && !hasHTTPRule(file.Services)) {
return nil
}
filename := file.GeneratedFilenamePrefix + "_http.pb.go"
g := gen.NewGeneratedFile(filename, file.GoImportPath)
g.P("// Code generated by protoc-gen-go-http. DO NOT EDIT.")
g.P("// versions:")
g.P(fmt.Sprintf("// - protoc-gen-go-http %s", release))
g.P("// - protoc ", protocVersion(gen))
if file.Proto.GetOptions().GetDeprecated() {
g.P("// ", file.Desc.Path(), " is a deprecated file.")
} else {
g.P("// source: ", file.Desc.Path())
}
g.P()
g.P("package ", file.GoPackageName)
g.P()
generateFileContent(gen, file, g, omitempty, omitemptyPrefix)
return g
}
// generateFileContent generates the kratos errors definitions, excluding the package statement.
func generateFileContent(gen *protogen.Plugin, file *protogen.File, g *protogen.GeneratedFile, omitempty bool, omitemptyPrefix string) {
if len(file.Services) == 0 {
return
}
g.P("// This is a compile-time assertion to ensure that this generated file")
g.P("// is compatible with the kratos package it is being compiled against.")
g.P("var _ = new(", contextPackage.Ident("Context"), ")")
g.P("var _ = ", bindingPackage.Ident("EncodeURL"))
g.P("const _ = ", transportHTTPPackage.Ident("SupportPackageIsVersion1"))
g.P()
for _, service := range file.Services {
genService(gen, file, g, service, omitempty, omitemptyPrefix)
}
}
func genService(_ *protogen.Plugin, file *protogen.File, g *protogen.GeneratedFile, service *protogen.Service, omitempty bool, omitemptyPrefix string) {
if service.Desc.Options().(*descriptorpb.ServiceOptions).GetDeprecated() {
g.P("//")
g.P(deprecationComment)
}
// HTTP Server.
sd := &serviceDesc{
ServiceType: service.GoName,
ServiceName: string(service.Desc.FullName()),
Metadata: file.Desc.Path(),
}
for _, method := range service.Methods {
if method.Desc.IsStreamingClient() || method.Desc.IsStreamingServer() {
continue
}
rule, ok := proto.GetExtension(method.Desc.Options(), annotations.E_Http).(*annotations.HttpRule)
if rule != nil && ok {
for _, bind := range rule.AdditionalBindings {
sd.Methods = append(sd.Methods, buildHTTPRule(g, service, method, bind, omitemptyPrefix))
}
sd.Methods = append(sd.Methods, buildHTTPRule(g, service, method, rule, omitemptyPrefix))
} else if !omitempty {
path := fmt.Sprintf("%s/%s/%s", omitemptyPrefix, service.Desc.FullName(), method.Desc.Name())
sd.Methods = append(sd.Methods, buildMethodDesc(g, method, http.MethodPost, path))
}
}
if len(sd.Methods) != 0 {
g.P(sd.execute())
}
}
func hasHTTPRule(services []*protogen.Service) bool {
for _, service := range services {
for _, method := range service.Methods {
if method.Desc.IsStreamingClient() || method.Desc.IsStreamingServer() {
continue
}
rule, ok := proto.GetExtension(method.Desc.Options(), annotations.E_Http).(*annotations.HttpRule)
if rule != nil && ok {
return true
}
}
}
return false
}
func buildHTTPRule(g *protogen.GeneratedFile, service *protogen.Service, m *protogen.Method, rule *annotations.HttpRule, omitemptyPrefix string) *methodDesc {
var (
path string
method string
body string
responseBody string
)
switch pattern := rule.Pattern.(type) {
case *annotations.HttpRule_Get:
path = pattern.Get
method = http.MethodGet
case *annotations.HttpRule_Put:
path = pattern.Put
method = http.MethodPut
case *annotations.HttpRule_Post:
path = pattern.Post
method = http.MethodPost
case *annotations.HttpRule_Delete:
path = pattern.Delete
method = http.MethodDelete
case *annotations.HttpRule_Patch:
path = pattern.Patch
method = http.MethodPatch
case *annotations.HttpRule_Custom:
path = pattern.Custom.Path
method = pattern.Custom.Kind
}
if method == "" {
method = http.MethodPost
}
if path == "" {
path = fmt.Sprintf("%s/%s/%s", omitemptyPrefix, service.Desc.FullName(), m.Desc.Name())
}
body = rule.Body
responseBody = rule.ResponseBody
md := buildMethodDesc(g, m, method, path)
if method == http.MethodGet || method == http.MethodDelete {
if body != "" {
_, _ = fmt.Fprintf(os.Stderr, "\u001B[31mWARN\u001B[m: %s %s body should not be declared.\n", method, path)
}
} else {
if body == "" {
_, _ = fmt.Fprintf(os.Stderr, "\u001B[31mWARN\u001B[m: %s %s does not declare a body.\n", method, path)
}
}
if body == "*" {
md.HasBody = true
md.Body = ""
} else if body != "" {
md.HasBody = true
md.Body = "." + camelCaseVars(body)
} else {
md.HasBody = false
}
if responseBody == "*" {
md.ResponseBody = ""
} else if responseBody != "" {
md.ResponseBody = "." + camelCaseVars(responseBody)
}
return md
}
func buildMethodDesc(g *protogen.GeneratedFile, m *protogen.Method, method, path string) *methodDesc {
defer func() { methodSets[m.GoName]++ }()
vars := buildPathVars(path)
for v, s := range vars {
fields := m.Input.Desc.Fields()
if s != nil {
path = replacePath(v, *s, path)
}
for _, field := range strings.Split(v, ".") {
if strings.TrimSpace(field) == "" {
continue
}
if strings.Contains(field, ":") {
field = strings.Split(field, ":")[0]
}
fd := fields.ByName(protoreflect.Name(field))
if fd == nil {
fmt.Fprintf(os.Stderr, "\u001B[31mERROR\u001B[m: The corresponding field '%s' declaration in message could not be found in '%s'\n", v, path)
os.Exit(2)
}
if fd.IsMap() {
fmt.Fprintf(os.Stderr, "\u001B[31mWARN\u001B[m: The field in path:'%s' shouldn't be a map.\n", v)
} else if fd.IsList() {
fmt.Fprintf(os.Stderr, "\u001B[31mWARN\u001B[m: The field in path:'%s' shouldn't be a list.\n", v)
} else if fd.Kind() == protoreflect.MessageKind || fd.Kind() == protoreflect.GroupKind {
fields = fd.Message().Fields()
}
}
}
comment := m.Comments.Leading.String() + m.Comments.Trailing.String()
if comment != "" {
comment = "// " + m.GoName + strings.TrimPrefix(strings.TrimSuffix(comment, "\n"), "//")
}
return &methodDesc{
Name: m.GoName,
OriginalName: string(m.Desc.Name()),
Num: methodSets[m.GoName],
Request: g.QualifiedGoIdent(m.Input.GoIdent),
Reply: g.QualifiedGoIdent(m.Output.GoIdent),
Comment: comment,
Path: path,
Method: method,
HasVars: len(vars) > 0,
}
}
func buildPathVars(path string) (res map[string]*string) {
if strings.HasSuffix(path, "/") {
fmt.Fprintf(os.Stderr, "\u001B[31mWARN\u001B[m: Path %s should not end with \"/\" \n", path)
}
pattern := regexp.MustCompile(`(?i){([a-z.0-9_\s]*)=?([^{}]*)}`)
matches := pattern.FindAllStringSubmatch(path, -1)
res = make(map[string]*string, len(matches))
for _, m := range matches {
name := strings.TrimSpace(m[1])
if len(name) > 1 && len(m[2]) > 0 {
res[name] = &m[2]
} else {
res[name] = nil
}
}
return
}
func replacePath(name string, value string, path string) string {
pattern := regexp.MustCompile(fmt.Sprintf(`(?i){([\s]*%s[\s]*)=?([^{}]*)}`, name))
idx := pattern.FindStringIndex(path)
if len(idx) > 0 {
path = fmt.Sprintf("%s{%s:%s}%s",
path[:idx[0]], // The start of the match
name,
strings.ReplaceAll(value, "*", ".*"),
path[idx[1]:],
)
}
return path
}
func camelCaseVars(s string) string {
subs := strings.Split(s, ".")
vars := make([]string, 0, len(subs))
for _, sub := range subs {
vars = append(vars, camelCase(sub))
}
return strings.Join(vars, ".")
}
// camelCase returns the CamelCased name.
// If there is an interior underscore followed by a lower case letter,
// drop the underscore and convert the letter to upper case.
// There is a remote possibility of this rewrite causing a name collision,
// but it's so remote we're prepared to pretend it's nonexistent - since the
// C++ generator lowercase names, it's extremely unlikely to have two fields
// with different capitalization.
// In short, _my_field_name_2 becomes XMyFieldName_2.
func camelCase(s string) string {
if s == "" {
return ""
}
t := make([]byte, 0, 32)
i := 0
if s[0] == '_' {
// Need a capital letter; drop the '_'.
t = append(t, 'X')
i++
}
// Invariant: if the next letter is lower case, it must be converted
// to upper case.
// That is, we process a word at a time, where words are marked by _ or
// upper case letter. Digits are treated as words.
for ; i < len(s); i++ {
c := s[i]
if c == '_' && i+1 < len(s) && isASCIILower(s[i+1]) {
continue // Skip the underscore in s.
}
if isASCIIDigit(c) {
t = append(t, c)
continue
}
// Assume we have a letter now - if not, it's a bogus identifier.
// The next word is a sequence of characters that must start upper case.
if isASCIILower(c) {
c ^= ' ' // Make it a capital letter.
}
t = append(t, c) // Guaranteed not lower case.
// Accept lower case sequence that follows.
for i+1 < len(s) && isASCIILower(s[i+1]) {
i++
t = append(t, s[i])
}
}
return string(t)
}
// Is c an ASCII lower-case letter?
func isASCIILower(c byte) bool {
return 'a' <= c && c <= 'z'
}
// Is c an ASCII digit?
func isASCIIDigit(c byte) bool {
return '0' <= c && c <= '9'
}
func protocVersion(gen *protogen.Plugin) string {
v := gen.Request.GetCompilerVersion()
if v == nil {
return "(unknown)"
}
var suffix string
if s := v.GetSuffix(); s != "" {
suffix = "-" + s
}
return fmt.Sprintf("v%d.%d.%d%s", v.GetMajor(), v.GetMinor(), v.GetPatch(), suffix)
}
const deprecationComment = "// Deprecated: Do not use."

@ -1,93 +0,0 @@
{{$svrType := .ServiceType}}
{{$svrName := .ServiceName}}
{{- range .MethodSets}}
const Operation{{$svrType}}{{.OriginalName}} = "/{{$svrName}}/{{.OriginalName}}"
{{- end}}
type {{.ServiceType}}HTTPServer interface {
{{- range .MethodSets}}
{{- if ne .Comment ""}}
{{.Comment}}
{{- end}}
{{.Name}}(context.Context, *{{.Request}}) (*{{.Reply}}, error)
{{- end}}
}
func Register{{.ServiceType}}HTTPServer(s *http.Server, srv {{.ServiceType}}HTTPServer) {
r := s.Route("/")
{{- range .Methods}}
r.{{.Method}}("{{.Path}}", _{{$svrType}}_{{.Name}}{{.Num}}_HTTP_Handler(srv))
{{- end}}
}
{{range .Methods}}
func _{{$svrType}}_{{.Name}}{{.Num}}_HTTP_Handler(srv {{$svrType}}HTTPServer) func(ctx http.Context) error {
return func(ctx http.Context) error {
var in {{.Request}}
{{- if .HasBody}}
if err := ctx.Bind(&in{{.Body}}); err != nil {
return err
}
{{- if not (eq .Body "")}}
if err := ctx.BindQuery(&in); err != nil {
return err
}
{{- end}}
{{- else}}
if err := ctx.BindQuery(&in{{.Body}}); err != nil {
return err
}
{{- end}}
{{- if .HasVars}}
if err := ctx.BindVars(&in); err != nil {
return err
}
{{- end}}
http.SetOperation(ctx,Operation{{$svrType}}{{.OriginalName}})
h := ctx.Middleware(func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.{{.Name}}(ctx, req.(*{{.Request}}))
})
out, err := h(ctx, &in)
if err != nil {
return err
}
reply := out.(*{{.Reply}})
return ctx.Result(200, reply{{.ResponseBody}})
}
}
{{end}}
type {{.ServiceType}}HTTPClient interface {
{{- range .MethodSets}}
{{.Name}}(ctx context.Context, req *{{.Request}}, opts ...http.CallOption) (rsp *{{.Reply}}, err error)
{{- end}}
}
type {{.ServiceType}}HTTPClientImpl struct{
cc *http.Client
}
func New{{.ServiceType}}HTTPClient (client *http.Client) {{.ServiceType}}HTTPClient {
return &{{.ServiceType}}HTTPClientImpl{client}
}
{{range .MethodSets}}
func (c *{{$svrType}}HTTPClientImpl) {{.Name}}(ctx context.Context, in *{{.Request}}, opts ...http.CallOption) (*{{.Reply}}, error) {
var out {{.Reply}}
pattern := "{{.Path}}"
path := binding.EncodeURL(pattern, in, {{not .HasBody}})
opts = append(opts, http.Operation(Operation{{$svrType}}{{.OriginalName}}))
opts = append(opts, http.PathTemplate(pattern))
{{if .HasBody -}}
err := c.cc.Invoke(ctx, "{{.Method}}", path, in{{.Body}}, &out{{.ResponseBody}}, opts...)
{{else -}}
err := c.cc.Invoke(ctx, "{{.Method}}", path, nil, &out{{.ResponseBody}}, opts...)
{{end -}}
if err != nil {
return nil, err
}
return &out, err
}
{{end}}

@ -1,87 +0,0 @@
package main
import (
"reflect"
"testing"
)
func TestNoParameters(t *testing.T) {
path := "/test/noparams"
m := buildPathVars(path)
if !reflect.DeepEqual(m, map[string]*string{}) {
t.Fatalf("Map should be empty")
}
}
func TestSingleParam(t *testing.T) {
path := "/test/{message.id}"
m := buildPathVars(path)
if !reflect.DeepEqual(len(m), 1) {
t.Fatalf("len(m) not is 1")
}
if m["message.id"] != nil {
t.Fatalf(`m["message.id"] should be empty`)
}
}
func TestTwoParametersReplacement(t *testing.T) {
path := "/test/{message.id}/{message.name=messages/*}"
m := buildPathVars(path)
if len(m) != 2 {
t.Fatal("len(m) should be 2")
}
if m["message.id"] != nil {
t.Fatal(`m["message.id"] should be nil`)
}
if m["message.name"] == nil {
t.Fatal(`m["message.name"] should not be nil`)
}
if *m["message.name"] != "messages/*" {
t.Fatal(`m["message.name"] should be "messages/*"`)
}
}
func TestNoReplacePath(t *testing.T) {
path := "/test/{message.id=test}"
if !reflect.DeepEqual(replacePath("message.id", "test", path), "/test/{message.id:test}") {
t.Fatal(`replacePath("message.id", "test", path) should be "/test/{message.id:test}"`)
}
path = "/test/{message.id=test/*}"
if !reflect.DeepEqual(replacePath("message.id", "test/*", path), "/test/{message.id:test/.*}") {
t.Fatal(`replacePath("message.id", "test/*", path) should be "/test/{message.id:test/.*}"`)
}
}
func TestReplacePath(t *testing.T) {
path := "/test/{message.id}/{message.name=messages/*}"
newPath := replacePath("message.name", "messages/*", path)
if !reflect.DeepEqual("/test/{message.id}/{message.name:messages/.*}", newPath) {
t.Fatal(`replacePath("message.name", "messages/*", path) should be "/test/{message.id}/{message.name:messages/.*}"`)
}
}
func TestIteration(t *testing.T) {
path := "/test/{message.id}/{message.name=messages/*}"
vars := buildPathVars(path)
for v, s := range vars {
if s != nil {
path = replacePath(v, *s, path)
}
}
if !reflect.DeepEqual("/test/{message.id}/{message.name:messages/.*}", path) {
t.Fatal(`replacePath("message.name", "messages/*", path) should be "/test/{message.id}/{message.name:messages/.*}"`)
}
}
func TestIterationMiddle(t *testing.T) {
path := "/test/{message.name=messages/*}/books"
vars := buildPathVars(path)
for v, s := range vars {
if s != nil {
path = replacePath(v, *s, path)
}
}
if !reflect.DeepEqual("/test/{message.name:messages/.*}/books", path) {
t.Fatal(`replacePath("message.name", "messages/*", path) should be "/test/{message.name:messages/.*}/books"`)
}
}

@ -1,35 +0,0 @@
package main
import (
"flag"
"fmt"
"google.golang.org/protobuf/compiler/protogen"
"google.golang.org/protobuf/types/pluginpb"
)
var (
showVersion = flag.Bool("version", false, "print the version and exit")
omitempty = flag.Bool("omitempty", true, "omit if google.api is empty")
omitemptyPrefix = flag.String("omitempty_prefix", "", "omit if google.api is empty")
)
func main() {
flag.Parse()
if *showVersion {
fmt.Printf("protoc-gen-go-http %v\n", release)
return
}
protogen.Options{
ParamFunc: flag.CommandLine.Set,
}.Run(func(gen *protogen.Plugin) error {
gen.SupportedFeatures = uint64(pluginpb.CodeGeneratorResponse_FEATURE_PROTO3_OPTIONAL)
for _, f := range gen.Files {
if !f.Generate {
continue
}
generateFile(gen, f, *omitempty, *omitemptyPrefix)
}
return nil
})
}

@ -1,52 +0,0 @@
package main
import (
"bytes"
_ "embed"
"strings"
"text/template"
)
//go:embed httpTemplate.tpl
var httpTemplate string
type serviceDesc struct {
ServiceType string // Greeter
ServiceName string // helloworld.Greeter
Metadata string // api/helloworld/helloworld.proto
Methods []*methodDesc
MethodSets map[string]*methodDesc
}
type methodDesc struct {
// method
Name string
OriginalName string // The parsed original name
Num int
Request string
Reply string
Comment string
// http_rule
Path string
Method string
HasVars bool
HasBody bool
Body string
ResponseBody string
}
func (s *serviceDesc) execute() string {
s.MethodSets = make(map[string]*methodDesc)
for _, m := range s.Methods {
s.MethodSets[m.Name] = m
}
buf := new(bytes.Buffer)
tmpl, err := template.New("http").Parse(strings.TrimSpace(httpTemplate))
if err != nil {
panic(err)
}
if err := tmpl.Execute(buf, s); err != nil {
panic(err)
}
return strings.Trim(buf.String(), "\r\n")
}

@ -1,4 +0,0 @@
package main
// release is the current protoc-gen-go-http version.
const release = "v2.6.3"

@ -1,3 +0,0 @@
ignore:
- "examples"
- "**/*.pb.go"

@ -1,25 +0,0 @@
# Config
## kubernetes
```shell
go get -u github.com/go-kratos/kratos/contrib/config/kubernetes/v2
```
## apollo
```shell
go get -u github.com/go-kratos/kratos/contrib/config/apollo/v2
```
## etcd
```shell
go get -u github.com/go-kratos/kratos/contrib/config/etcd/v2
```
## nacos
```shell
go get -u github.com/go-kratos/kratos/contrib/config/nacos/v2
```

@ -1,158 +0,0 @@
package config
import (
"context"
"errors"
"reflect"
"sync"
"time"
// init encoding
_ "github.com/go-kratos/kratos/v2/encoding/json"
_ "github.com/go-kratos/kratos/v2/encoding/proto"
_ "github.com/go-kratos/kratos/v2/encoding/xml"
_ "github.com/go-kratos/kratos/v2/encoding/yaml"
"github.com/go-kratos/kratos/v2/log"
)
var _ Config = (*config)(nil)
var (
// ErrNotFound is key not found.
ErrNotFound = errors.New("key not found")
// ErrTypeAssert is type assert error.
ErrTypeAssert = errors.New("type assert error")
)
// Observer is config observer.
type Observer func(string, Value)
// Config is a config interface.
type Config interface {
Load() error
Scan(v interface{}) error
Value(key string) Value
Watch(key string, o Observer) error
Close() error
}
type config struct {
opts options
reader Reader
cached sync.Map
observers sync.Map
watchers []Watcher
}
// New a config with options.
func New(opts ...Option) Config {
o := options{
decoder: defaultDecoder,
resolver: defaultResolver,
}
for _, opt := range opts {
opt(&o)
}
return &config{
opts: o,
reader: newReader(o),
}
}
func (c *config) watch(w Watcher) {
for {
kvs, err := w.Next()
if err != nil {
if errors.Is(err, context.Canceled) {
log.Infof("watcher's ctx cancel : %v", err)
return
}
time.Sleep(time.Second)
log.Errorf("failed to watch next config: %v", err)
continue
}
if err := c.reader.Merge(kvs...); err != nil {
log.Errorf("failed to merge next config: %v", err)
continue
}
if err := c.reader.Resolve(); err != nil {
log.Errorf("failed to resolve next config: %v", err)
continue
}
c.cached.Range(func(key, value interface{}) bool {
k := key.(string)
v := value.(Value)
if n, ok := c.reader.Value(k); ok && reflect.TypeOf(n.Load()) == reflect.TypeOf(v.Load()) && !reflect.DeepEqual(n.Load(), v.Load()) {
v.Store(n.Load())
if o, ok := c.observers.Load(k); ok {
o.(Observer)(k, v)
}
}
return true
})
}
}
func (c *config) Load() error {
for _, src := range c.opts.sources {
kvs, err := src.Load()
if err != nil {
return err
}
for _, v := range kvs {
log.Debugf("config loaded: %s format: %s", v.Key, v.Format)
}
if err = c.reader.Merge(kvs...); err != nil {
log.Errorf("failed to merge config source: %v", err)
return err
}
w, err := src.Watch()
if err != nil {
log.Errorf("failed to watch config source: %v", err)
return err
}
c.watchers = append(c.watchers, w)
go c.watch(w)
}
if err := c.reader.Resolve(); err != nil {
log.Errorf("failed to resolve config source: %v", err)
return err
}
return nil
}
func (c *config) Value(key string) Value {
if v, ok := c.cached.Load(key); ok {
return v.(Value)
}
if v, ok := c.reader.Value(key); ok {
c.cached.Store(key, v)
return v
}
return &errValue{err: ErrNotFound}
}
func (c *config) Scan(v interface{}) error {
data, err := c.reader.Source()
if err != nil {
return err
}
return unmarshalJSON(data, v)
}
func (c *config) Watch(key string, o Observer) error {
if v := c.Value(key); v.Load() == nil {
return ErrNotFound
}
c.observers.Store(key, o)
return nil
}
func (c *config) Close() error {
for _, w := range c.watchers {
if err := w.Stop(); err != nil {
return err
}
}
return nil
}

@ -1,184 +0,0 @@
package config
import (
"errors"
"testing"
)
const (
_testJSON = `
{
"server":{
"http":{
"addr":"0.0.0.0",
"port":80,
"timeout":0.5,
"enable_ssl":true
},
"grpc":{
"addr":"0.0.0.0",
"port":10080,
"timeout":0.2
}
},
"data":{
"database":{
"driver":"mysql",
"source":"root:root@tcp(127.0.0.1:3306)/karta_id?parseTime=true"
}
},
"endpoints":[
"www.aaa.com",
"www.bbb.org"
]
}`
)
type testConfigStruct struct {
Server struct {
HTTP struct {
Addr string `json:"addr"`
Port int `json:"port"`
Timeout float64 `json:"timeout"`
EnableSSL bool `json:"enable_ssl"`
} `json:"http"`
GRPC struct {
Addr string `json:"addr"`
Port int `json:"port"`
Timeout float64 `json:"timeout"`
} `json:"grpc"`
} `json:"server"`
Data struct {
Database struct {
Driver string `json:"driver"`
Source string `json:"source"`
} `json:"database"`
} `json:"data"`
Endpoints []string `json:"endpoints"`
}
type testJSONSource struct {
data string
sig chan struct{}
err chan struct{}
}
func newTestJSONSource(data string) *testJSONSource {
return &testJSONSource{data: data, sig: make(chan struct{}), err: make(chan struct{})}
}
func (p *testJSONSource) Load() ([]*KeyValue, error) {
kv := &KeyValue{
Key: "json",
Value: []byte(p.data),
Format: "json",
}
return []*KeyValue{kv}, nil
}
func (p *testJSONSource) Watch() (Watcher, error) {
return newTestWatcher(p.sig, p.err), nil
}
type testWatcher struct {
sig chan struct{}
err chan struct{}
exit chan struct{}
}
func newTestWatcher(sig, err chan struct{}) Watcher {
return &testWatcher{sig: sig, err: err, exit: make(chan struct{})}
}
func (w *testWatcher) Next() ([]*KeyValue, error) {
select {
case <-w.sig:
return nil, nil
case <-w.err:
return nil, errors.New("error")
case <-w.exit:
return nil, nil
}
}
func (w *testWatcher) Stop() error {
close(w.exit)
return nil
}
func TestConfig(t *testing.T) {
var (
err error
httpAddr = "0.0.0.0"
httpTimeout = 0.5
grpcPort = 10080
endpoint1 = "www.aaa.com"
databaseDriver = "mysql"
)
c := New(
WithSource(newTestJSONSource(_testJSON)),
WithDecoder(defaultDecoder),
WithResolver(defaultResolver),
)
err = c.Close()
if err != nil {
t.Fatal(err)
}
jSource := newTestJSONSource(_testJSON)
opts := options{
sources: []Source{jSource},
decoder: defaultDecoder,
resolver: defaultResolver,
}
cf := &config{}
cf.opts = opts
cf.reader = newReader(opts)
err = cf.Load()
if err != nil {
t.Fatal(err)
}
driver, err := cf.Value("data.database.driver").String()
if err != nil {
t.Fatal(err)
}
if databaseDriver != driver {
t.Fatal("databaseDriver is not equal to val")
}
err = cf.Watch("endpoints", func(key string, value Value) {
})
if err != nil {
t.Fatal(err)
}
jSource.sig <- struct{}{}
jSource.err <- struct{}{}
var testConf testConfigStruct
err = cf.Scan(&testConf)
if err != nil {
t.Fatal(err)
}
if httpAddr != testConf.Server.HTTP.Addr {
t.Errorf("testConf.Server.HTTP.Addr want: %s, got: %s", httpAddr, testConf.Server.HTTP.Addr)
}
if httpTimeout != testConf.Server.HTTP.Timeout {
t.Errorf("testConf.Server.HTTP.Timeout want: %.1f, got: %.1f", httpTimeout, testConf.Server.HTTP.Timeout)
}
if !testConf.Server.HTTP.EnableSSL {
t.Error("testConf.Server.HTTP.EnableSSL is not equal to true")
}
if grpcPort != testConf.Server.GRPC.Port {
t.Errorf("testConf.Server.GRPC.Port want: %d, got: %d", grpcPort, testConf.Server.GRPC.Port)
}
if endpoint1 != testConf.Endpoints[0] {
t.Errorf("testConf.Endpoints[0] want: %s, got: %s", endpoint1, testConf.Endpoints[0])
}
if len(testConf.Endpoints) != 2 {
t.Error("len(testConf.Endpoints) is not equal to 2")
}
}

67
config/env/env.go vendored

@ -1,67 +0,0 @@
package env
import (
"os"
"strings"
"github.com/go-kratos/kratos/v2/config"
)
type env struct {
prefixes []string
}
func NewSource(prefixes ...string) config.Source {
return &env{prefixes: prefixes}
}
func (e *env) Load() (kv []*config.KeyValue, err error) {
return e.load(os.Environ()), nil
}
func (e *env) load(envs []string) []*config.KeyValue {
var kv []*config.KeyValue
for _, env := range envs {
var k, v string
subs := strings.SplitN(env, "=", 2) //nolint:gomnd
k = subs[0]
if len(subs) > 1 {
v = subs[1]
}
if len(e.prefixes) > 0 {
p, ok := matchPrefix(e.prefixes, k)
if !ok || len(p) == len(k) {
continue
}
// trim prefix
k = strings.TrimPrefix(k, p)
k = strings.TrimPrefix(k, "_")
}
if len(k) != 0 {
kv = append(kv, &config.KeyValue{
Key: k,
Value: []byte(v),
})
}
}
return kv
}
func (e *env) Watch() (config.Watcher, error) {
w, err := NewWatcher()
if err != nil {
return nil, err
}
return w, nil
}
func matchPrefix(prefixes []string, s string) (string, bool) {
for _, p := range prefixes {
if strings.HasPrefix(s, p) {
return p, true
}
}
return "", false
}

@ -1,429 +0,0 @@
package env
import (
"os"
"path/filepath"
"reflect"
"testing"
"github.com/go-kratos/kratos/v2/config"
"github.com/go-kratos/kratos/v2/config/file"
)
const _testJSON = `
{
"test":{
"server":{
"name":"${SERVICE_NAME}",
"addr":"${ADDR:127.0.0.1}",
"port":"${PORT:8080}"
}
},
"foo":[
{
"name":"Tom",
"age":"${AGE}"
}
]
}`
func TestEnvWithPrefix(t *testing.T) {
var (
path = filepath.Join(t.TempDir(), "test_config")
filename = filepath.Join(path, "test.json")
data = []byte(_testJSON)
)
defer os.Remove(path)
if err := os.MkdirAll(path, 0o700); err != nil {
t.Error(err)
}
if err := os.WriteFile(filename, data, 0o666); err != nil {
t.Error(err)
}
// set env
prefix1, prefix2 := "KRATOS_", "FOO"
envs := map[string]string{
prefix1 + "SERVICE_NAME": "kratos_app",
prefix2 + "ADDR": "192.168.0.1",
prefix1 + "AGE": "20",
// only prefix
prefix2: "foo",
prefix2 + "_": "foo_",
}
for k, v := range envs {
os.Setenv(k, v)
}
c := config.New(config.WithSource(
file.NewSource(path),
NewSource(prefix1, prefix2),
))
if err := c.Load(); err != nil {
t.Fatal(err)
}
tests := []struct {
name string
path string
expect interface{}
}{
{
name: "test $KEY",
path: "test.server.name",
expect: "kratos_app",
},
{
name: "test ${KEY:DEFAULT} without default",
path: "test.server.addr",
expect: "192.168.0.1",
},
{
name: "test ${KEY:DEFAULT} with default",
path: "test.server.port",
expect: "8080",
},
{
name: "test ${KEY} in array",
path: "foo",
expect: []interface{}{
map[string]interface{}{
"name": "Tom",
"age": "20",
},
},
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
var err error
v := c.Value(test.path)
if v.Load() != nil {
var actual interface{}
switch test.expect.(type) {
case int:
if actual, err = v.Int(); err == nil {
if !reflect.DeepEqual(test.expect.(int), int(actual.(int64))) {
t.Errorf("expect %v, actual %v", test.expect, actual)
}
}
case string:
if actual, err = v.String(); err == nil {
if !reflect.DeepEqual(test.expect.(string), actual.(string)) {
t.Errorf(`expect %v, actual %v`, test.expect, actual)
}
}
case bool:
if actual, err = v.Bool(); err == nil {
if !reflect.DeepEqual(test.expect.(bool), actual.(bool)) {
t.Errorf(`expect %v, actual %v`, test.expect, actual)
}
}
case float64:
if actual, err = v.Float(); err == nil {
if !reflect.DeepEqual(test.expect.(float64), actual.(float64)) {
t.Errorf(`expect %v, actual %v`, test.expect, actual)
}
}
default:
actual = v.Load()
if !reflect.DeepEqual(test.expect, actual) {
t.Logf("\nexpect: %#v\nactural: %#v", test.expect, actual)
t.Fail()
}
}
if err != nil {
t.Error(err)
}
} else {
t.Error("value path not found")
}
})
}
}
func TestEnvWithoutPrefix(t *testing.T) {
var (
path = filepath.Join(t.TempDir(), "test_config")
filename = filepath.Join(path, "test.json")
data = []byte(_testJSON)
)
defer os.Remove(path)
if err := os.MkdirAll(path, 0o700); err != nil {
t.Error(err)
}
if err := os.WriteFile(filename, data, 0o666); err != nil {
t.Error(err)
}
// set env
envs := map[string]string{
"SERVICE_NAME": "kratos_app",
"ADDR": "192.168.0.1",
"AGE": "20",
}
for k, v := range envs {
os.Setenv(k, v)
}
c := config.New(config.WithSource(
NewSource(),
file.NewSource(path),
))
if err := c.Load(); err != nil {
t.Fatal(err)
}
tests := []struct {
name string
path string
expect interface{}
}{
{
name: "test $KEY",
path: "test.server.name",
expect: "kratos_app",
},
{
name: "test ${KEY:DEFAULT} without default",
path: "test.server.addr",
expect: "192.168.0.1",
},
{
name: "test ${KEY:DEFAULT} with default",
path: "test.server.port",
expect: "8080",
},
{
name: "test ${KEY} in array",
path: "foo",
expect: []interface{}{
map[string]interface{}{
"name": "Tom",
"age": "20",
},
},
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
var err error
v := c.Value(test.path)
if v.Load() != nil {
var actual interface{}
switch test.expect.(type) {
case int:
if actual, err = v.Int(); err == nil {
if !reflect.DeepEqual(test.expect.(int), int(actual.(int64))) {
t.Errorf("expect %v, actual %v", test.expect, actual)
}
}
case string:
if actual, err = v.String(); err == nil {
if !reflect.DeepEqual(test.expect.(string), actual.(string)) {
t.Errorf(`expect %v, actual %v`, test.expect, actual)
}
}
case bool:
if actual, err = v.Bool(); err == nil {
if !reflect.DeepEqual(test.expect.(bool), actual.(bool)) {
t.Errorf(`expect %v, actual %v`, test.expect, actual)
}
}
case float64:
if actual, err = v.Float(); err == nil {
if !reflect.DeepEqual(test.expect.(float64), actual.(float64)) {
t.Errorf(`expect %v, actual %v`, test.expect, actual)
}
}
default:
actual = v.Load()
if !reflect.DeepEqual(test.expect, actual) {
t.Logf("\nexpect: %#v\nactural: %#v", test.expect, actual)
t.Fail()
}
}
if err != nil {
t.Error(err)
}
} else {
t.Error("value path not found")
}
})
}
}
func Test_env_load(t *testing.T) {
type fields struct {
prefixes []string
}
type args struct {
envStrings []string
}
tests := []struct {
name string
fields fields
args args
want []*config.KeyValue
}{
{
name: "without prefixes",
fields: fields{
prefixes: nil,
},
args: args{
envStrings: []string{
"SERVICE_NAME=kratos_app",
"ADDR=192.168.0.1",
"AGE=20",
},
},
want: []*config.KeyValue{
{Key: "SERVICE_NAME", Value: []byte("kratos_app"), Format: ""},
{Key: "ADDR", Value: []byte("192.168.0.1"), Format: ""},
{Key: "AGE", Value: []byte("20"), Format: ""},
},
},
{
name: "empty prefix",
fields: fields{
prefixes: []string{""},
},
args: args{
envStrings: []string{
"__SERVICE_NAME=kratos_app",
"__ADDR=192.168.0.1",
"__AGE=20",
},
},
want: []*config.KeyValue{
{Key: "_SERVICE_NAME", Value: []byte("kratos_app"), Format: ""},
{Key: "_ADDR", Value: []byte("192.168.0.1"), Format: ""},
{Key: "_AGE", Value: []byte("20"), Format: ""},
},
},
{
name: "underscore prefix",
fields: fields{
prefixes: []string{"_"},
},
args: args{
envStrings: []string{
"__SERVICE_NAME=kratos_app",
"__ADDR=192.168.0.1",
"__AGE=20",
},
},
want: []*config.KeyValue{
{Key: "SERVICE_NAME", Value: []byte("kratos_app"), Format: ""},
{Key: "ADDR", Value: []byte("192.168.0.1"), Format: ""},
{Key: "AGE", Value: []byte("20"), Format: ""},
},
},
{
name: "with prefixes",
fields: fields{
prefixes: []string{"KRATOS_", "FOO"},
},
args: args{
envStrings: []string{
"KRATOS_SERVICE_NAME=kratos_app",
"KRATOS_ADDR=192.168.0.1",
"FOO_AGE=20",
},
},
want: []*config.KeyValue{
{Key: "SERVICE_NAME", Value: []byte("kratos_app"), Format: ""},
{Key: "ADDR", Value: []byte("192.168.0.1"), Format: ""},
{Key: "AGE", Value: []byte("20"), Format: ""},
},
},
{
name: "should not panic #1",
fields: fields{
prefixes: []string{"FOO"},
},
args: args{
envStrings: []string{
"FOO=123",
},
},
want: nil,
},
{
name: "should not panic #2",
fields: fields{
prefixes: []string{"FOO=1"},
},
args: args{
envStrings: []string{
"FOO=123",
},
},
want: nil,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
e := &env{
prefixes: tt.fields.prefixes,
}
got := e.load(tt.args.envStrings)
if !reflect.DeepEqual(tt.want, got) {
t.Errorf("env.load() = %v, want %v", got, tt.want)
}
})
}
}
func Test_matchPrefix(t *testing.T) {
type args struct {
prefixes []string
s string
}
tests := []struct {
name string
args args
want string
wantOk bool
}{
{args: args{prefixes: nil, s: "foo=123"}, want: "", wantOk: false},
{args: args{prefixes: []string{""}, s: "foo=123"}, want: "", wantOk: true},
{args: args{prefixes: []string{"foo"}, s: "foo=123"}, want: "foo", wantOk: true},
{args: args{prefixes: []string{"foo=1"}, s: "foo=123"}, want: "foo=1", wantOk: true},
{args: args{prefixes: []string{"foo=1234"}, s: "foo=123"}, want: "", wantOk: false},
{args: args{prefixes: []string{"bar"}, s: "foo=123"}, want: "", wantOk: false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, gotOk := matchPrefix(tt.args.prefixes, tt.args.s)
if got != tt.want {
t.Errorf("matchPrefix() got = %v, want %v", got, tt.want)
}
if gotOk != tt.wantOk {
t.Errorf("matchPrefix() gotOk = %v, wantOk %v", gotOk, tt.wantOk)
}
})
}
}
func Test_env_watch(t *testing.T) {
prefixes := []string{"BAR", "FOO"}
source := NewSource(prefixes...)
w, err := source.Watch()
if err != nil {
t.Errorf("expect no err, got %v", err)
}
_ = w.Stop()
}

@ -1,30 +0,0 @@
package env
import (
"context"
"github.com/go-kratos/kratos/v2/config"
)
var _ config.Watcher = (*watcher)(nil)
type watcher struct {
ctx context.Context
cancel context.CancelFunc
}
func NewWatcher() (config.Watcher, error) {
ctx, cancel := context.WithCancel(context.Background())
return &watcher{ctx: ctx, cancel: cancel}, nil
}
// Next will be blocked until the Stop method is called
func (w *watcher) Next() ([]*config.KeyValue, error) {
<-w.ctx.Done()
return nil, w.ctx.Err()
}
func (w *watcher) Stop() error {
w.cancel()
return nil
}

@ -1,32 +0,0 @@
package env
import (
"testing"
)
func Test_watcher_next(t *testing.T) {
t.Run("next after stop should return err", func(t *testing.T) {
w, err := NewWatcher()
if err != nil {
t.Errorf("expect no error, got %v", err)
}
_ = w.Stop()
_, err = w.Next()
if err == nil {
t.Error("expect error, actual nil")
}
})
}
func Test_watcher_stop(t *testing.T) {
t.Run("stop multiple times should not panic", func(t *testing.T) {
w, err := NewWatcher()
if err != nil {
t.Errorf("expect no error, got %v", err)
}
_ = w.Stop()
_ = w.Stop()
})
}

@ -1,80 +0,0 @@
package file
import (
"io"
"os"
"path/filepath"
"strings"
"github.com/go-kratos/kratos/v2/config"
)
var _ config.Source = (*file)(nil)
type file struct {
path string
}
// NewSource new a file source.
func NewSource(path string) config.Source {
return &file{path: path}
}
func (f *file) loadFile(path string) (*config.KeyValue, error) {
file, err := os.Open(path)
if err != nil {
return nil, err
}
defer file.Close()
data, err := io.ReadAll(file)
if err != nil {
return nil, err
}
info, err := file.Stat()
if err != nil {
return nil, err
}
return &config.KeyValue{
Key: info.Name(),
Format: format(info.Name()),
Value: data,
}, nil
}
func (f *file) loadDir(path string) (kvs []*config.KeyValue, err error) {
files, err := os.ReadDir(path)
if err != nil {
return nil, err
}
for _, file := range files {
// ignore hidden files
if file.IsDir() || strings.HasPrefix(file.Name(), ".") {
continue
}
kv, err := f.loadFile(filepath.Join(path, file.Name()))
if err != nil {
return nil, err
}
kvs = append(kvs, kv)
}
return
}
func (f *file) Load() (kvs []*config.KeyValue, err error) {
fi, err := os.Stat(f.path)
if err != nil {
return nil, err
}
if fi.IsDir() {
return f.loadDir(f.path)
}
kv, err := f.loadFile(f.path)
if err != nil {
return nil, err
}
return []*config.KeyValue{kv}, nil
}
func (f *file) Watch() (config.Watcher, error) {
return newWatcher(f)
}

@ -1,341 +0,0 @@
package file
import (
"errors"
"os"
"path/filepath"
"reflect"
"sync"
"testing"
"time"
"github.com/go-kratos/kratos/v2/config"
)
const (
_testJSON = `
{
"test":{
"settings":{
"int_key":1000,
"float_key":1000.1,
"duration_key":10000,
"string_key":"string_value"
},
"server":{
"addr":"127.0.0.1",
"port":8000
}
},
"foo":[
{
"name":"nihao",
"age":18
},
{
"name":"nihao",
"age":18
}
]
}`
_testJSONUpdate = `
{
"test":{
"settings":{
"int_key":1000,
"float_key":1000.1,
"duration_key":10000,
"string_key":"string_value"
},
"server":{
"addr":"127.0.0.1",
"port":8000
}
},
"foo":[
{
"name":"nihao",
"age":18
},
{
"name":"nihao",
"age":18
}
],
"bar":{
"event":"update"
}
}`
// _testYaml = `
//Foo:
// bar :
// - {name: nihao,age: 1}
// - {name: nihao,age: 1}
//
//
//`
)
//func TestScan(t *testing.T) {
//
//}
func TestFile(t *testing.T) {
var (
path = filepath.Join(t.TempDir(), "test_config")
file = filepath.Join(path, "test.json")
data = []byte(_testJSON)
)
defer os.Remove(path)
if err := os.MkdirAll(path, 0o700); err != nil {
t.Error(err)
}
if err := os.WriteFile(file, data, 0o666); err != nil {
t.Error(err)
}
testSource(t, file, data)
testSource(t, path, data)
testWatchFile(t, file)
testWatchDir(t, path, file)
}
func testWatchFile(t *testing.T, path string) {
t.Log(path)
s := NewSource(path)
watch, err := s.Watch()
if err != nil {
t.Error(err)
}
f, err := os.OpenFile(path, os.O_RDWR, 0)
if err != nil {
t.Error(err)
}
defer f.Close()
_, err = f.WriteString(_testJSONUpdate)
if err != nil {
t.Error(err)
}
kvs, err := watch.Next()
if err != nil {
t.Errorf("watch.Next() error(%v)", err)
}
if !reflect.DeepEqual(string(kvs[0].Value), _testJSONUpdate) {
t.Errorf("string(kvs[0].Value(%v) is not equal to _testJSONUpdate(%v)", kvs[0].Value, _testJSONUpdate)
}
newFilepath := filepath.Join(filepath.Dir(path), "test1.json")
if err = os.Rename(path, newFilepath); err != nil {
t.Error(err)
}
kvs, err = watch.Next()
if err == nil {
t.Errorf("watch.Next() error(%v)", err)
}
if kvs != nil {
t.Errorf("watch.Next() error(%v)", err)
}
err = watch.Stop()
if err != nil {
t.Errorf("watch.Stop() error(%v)", err)
}
if err := os.Rename(newFilepath, path); err != nil {
t.Error(err)
}
}
func testWatchDir(t *testing.T, path, file string) {
t.Log(path)
t.Log(file)
s := NewSource(path)
watch, err := s.Watch()
if err != nil {
t.Error(err)
}
f, err := os.OpenFile(file, os.O_RDWR, 0)
if err != nil {
t.Error(err)
}
defer f.Close()
_, err = f.WriteString(_testJSONUpdate)
if err != nil {
t.Error(err)
}
kvs, err := watch.Next()
if err != nil {
t.Errorf("watch.Next() error(%v)", err)
}
if !reflect.DeepEqual(string(kvs[0].Value), _testJSONUpdate) {
t.Errorf("string(kvs[0].Value(%s) is not equal to _testJSONUpdate(%v)", kvs[0].Value, _testJSONUpdate)
}
}
func testSource(t *testing.T, path string, data []byte) {
t.Log(path)
s := NewSource(path)
kvs, err := s.Load()
if err != nil {
t.Error(err)
}
if string(kvs[0].Value) != string(data) {
t.Errorf("no expected: %s, but got: %s", kvs[0].Value, data)
}
}
func TestConfig(t *testing.T) {
path := filepath.Join(t.TempDir(), "test_config.json")
defer os.Remove(path)
if err := os.WriteFile(path, []byte(_testJSON), 0o666); err != nil {
t.Error(err)
}
c := config.New(config.WithSource(
NewSource(path),
))
testScan(t, c)
testConfig(t, c)
}
func testConfig(t *testing.T, c config.Config) {
expected := map[string]interface{}{
"test.settings.int_key": int64(1000),
"test.settings.float_key": 1000.1,
"test.settings.string_key": "string_value",
"test.settings.duration_key": time.Duration(10000),
"test.server.addr": "127.0.0.1",
"test.server.port": int64(8000),
}
if err := c.Load(); err != nil {
t.Error(err)
}
for key, value := range expected {
switch value.(type) {
case int64:
if v, err := c.Value(key).Int(); err != nil {
t.Error(key, value, err)
} else if v != value {
t.Errorf("no expect key: %s value: %v, but got: %v", key, value, v)
}
case float64:
if v, err := c.Value(key).Float(); err != nil {
t.Error(key, value, err)
} else if v != value {
t.Errorf("no expect key: %s value: %v, but got: %v", key, value, v)
}
case string:
if v, err := c.Value(key).String(); err != nil {
t.Error(key, value, err)
} else if v != value {
t.Errorf("no expect key: %s value: %v, but got: %v", key, value, v)
}
case time.Duration:
if v, err := c.Value(key).Duration(); err != nil {
t.Error(key, value, err)
} else if v != value {
t.Errorf("no expect key: %s value: %v, but got: %v", key, value, v)
}
}
}
// scan
var settings struct {
IntKey int64 `json:"int_key"`
FloatKey float64 `json:"float_key"`
StringKey string `json:"string_key"`
DurationKey time.Duration `json:"duration_key"`
}
if err := c.Value("test.settings").Scan(&settings); err != nil {
t.Error(err)
}
if v := expected["test.settings.int_key"]; settings.IntKey != v {
t.Errorf("no expect int_key value: %v, but got: %v", settings.IntKey, v)
}
if v := expected["test.settings.float_key"]; settings.FloatKey != v {
t.Errorf("no expect float_key value: %v, but got: %v", settings.FloatKey, v)
}
if v := expected["test.settings.string_key"]; settings.StringKey != v {
t.Errorf("no expect string_key value: %v, but got: %v", settings.StringKey, v)
}
if v := expected["test.settings.duration_key"]; settings.DurationKey != v {
t.Errorf("no expect duration_key value: %v, but got: %v", settings.DurationKey, v)
}
// not found
if _, err := c.Value("not_found_key").Bool(); errors.Is(err, config.ErrNotFound) {
t.Logf("not_found_key not match: %v", err)
}
}
func testScan(t *testing.T, c config.Config) {
type TestJSON struct {
Test struct {
Settings struct {
IntKey int `json:"int_key"`
FloatKey float64 `json:"float_key"`
DurationKey int `json:"duration_key"`
StringKey string `json:"string_key"`
} `json:"settings"`
Server struct {
Addr string `json:"addr"`
Port int `json:"port"`
} `json:"server"`
} `json:"test"`
Foo []struct {
Name string `json:"name"`
Age int `json:"age"`
} `json:"foo"`
}
var conf TestJSON
if err := c.Load(); err != nil {
t.Error(err)
}
if err := c.Scan(&conf); err != nil {
t.Error(err)
}
t.Log(conf)
}
func TestMergeDataRace(t *testing.T) {
path := filepath.Join(t.TempDir(), "test_config.json")
defer os.Remove(path)
if err := os.WriteFile(path, []byte(_testJSON), 0o666); err != nil {
t.Error(err)
}
c := config.New(config.WithSource(
NewSource(path),
))
const count = 80
wg := &sync.WaitGroup{}
wg.Add(2)
startCh := make(chan struct{})
go func() {
defer wg.Done()
<-startCh
for i := 0; i < count; i++ {
var conf struct{}
if err := c.Scan(&conf); err != nil {
t.Error(err)
}
}
}()
go func() {
defer wg.Done()
<-startCh
for i := 0; i < count; i++ {
if err := c.Load(); err != nil {
t.Error(err)
}
}
}()
close(startCh)
wg.Wait()
}

@ -1,10 +0,0 @@
package file
import "strings"
func format(name string) string {
if p := strings.Split(name, "."); len(p) > 1 {
return p[len(p)-1]
}
return ""
}

@ -1,43 +0,0 @@
package file
import (
"testing"
)
func TestFormat(t *testing.T) {
tests := []struct {
input string
expect string
}{
{
input: "",
expect: "",
},
{
input: " ",
expect: "",
},
{
input: ".",
expect: "",
},
{
input: "a.",
expect: "",
},
{
input: ".b",
expect: "b",
},
{
input: "a.b",
expect: "b",
},
}
for _, v := range tests {
content := format(v.input)
if got, want := content, v.expect; got != want {
t.Errorf("expect %v,got %v", want, got)
}
}
}

@ -1,68 +0,0 @@
package file
import (
"context"
"os"
"path/filepath"
"github.com/fsnotify/fsnotify"
"github.com/go-kratos/kratos/v2/config"
)
var _ config.Watcher = (*watcher)(nil)
type watcher struct {
f *file
fw *fsnotify.Watcher
ctx context.Context
cancel context.CancelFunc
}
func newWatcher(f *file) (config.Watcher, error) {
fw, err := fsnotify.NewWatcher()
if err != nil {
return nil, err
}
if err := fw.Add(f.path); err != nil {
return nil, err
}
ctx, cancel := context.WithCancel(context.Background())
return &watcher{f: f, fw: fw, ctx: ctx, cancel: cancel}, nil
}
func (w *watcher) Next() ([]*config.KeyValue, error) {
select {
case <-w.ctx.Done():
return nil, w.ctx.Err()
case event := <-w.fw.Events:
if event.Op == fsnotify.Rename {
if _, err := os.Stat(event.Name); err == nil || os.IsExist(err) {
if err := w.fw.Add(event.Name); err != nil {
return nil, err
}
}
}
fi, err := os.Stat(w.f.path)
if err != nil {
return nil, err
}
path := w.f.path
if fi.IsDir() {
path = filepath.Join(w.f.path, filepath.Base(event.Name))
}
kv, err := w.f.loadFile(path)
if err != nil {
return nil, err
}
return []*config.KeyValue{kv}, nil
case err := <-w.fw.Errors:
return nil, err
}
}
func (w *watcher) Stop() error {
w.cancel()
return w.fw.Close()
}

@ -1,133 +0,0 @@
package config
import (
"fmt"
"regexp"
"strings"
"github.com/go-kratos/kratos/v2/encoding"
"github.com/go-kratos/kratos/v2/log"
)
// Decoder is config decoder.
type Decoder func(*KeyValue, map[string]interface{}) error
// Resolver resolve placeholder in config.
type Resolver func(map[string]interface{}) error
// Option is config option.
type Option func(*options)
type options struct {
sources []Source
decoder Decoder
resolver Resolver
}
// WithSource with config source.
func WithSource(s ...Source) Option {
return func(o *options) {
o.sources = s
}
}
// WithDecoder with config decoder.
// DefaultDecoder behavior:
// If KeyValue.Format is non-empty, then KeyValue.Value will be deserialized into map[string]interface{}
// and stored in the config cache(map[string]interface{})
// if KeyValue.Format is empty,{KeyValue.Key : KeyValue.Value} will be stored in config cache(map[string]interface{})
func WithDecoder(d Decoder) Option {
return func(o *options) {
o.decoder = d
}
}
// WithResolver with config resolver.
func WithResolver(r Resolver) Option {
return func(o *options) {
o.resolver = r
}
}
// WithLogger with config logger.
// Deprecated: use global logger instead.
func WithLogger(_ log.Logger) Option {
return func(o *options) {}
}
// defaultDecoder decode config from source KeyValue
// to target map[string]interface{} using src.Format codec.
func defaultDecoder(src *KeyValue, target map[string]interface{}) error {
if src.Format == "" {
// expand key "aaa.bbb" into map[aaa]map[bbb]interface{}
keys := strings.Split(src.Key, ".")
for i, k := range keys {
if i == len(keys)-1 {
target[k] = src.Value
} else {
sub := make(map[string]interface{})
target[k] = sub
target = sub
}
}
return nil
}
if codec := encoding.GetCodec(src.Format); codec != nil {
return codec.Unmarshal(src.Value, &target)
}
return fmt.Errorf("unsupported key: %s format: %s", src.Key, src.Format)
}
// defaultResolver resolve placeholder in map value,
// placeholder format in ${key:default}.
func defaultResolver(input map[string]interface{}) error {
mapper := func(name string) string {
args := strings.SplitN(strings.TrimSpace(name), ":", 2) //nolint:gomnd
if v, has := readValue(input, args[0]); has {
s, _ := v.String()
return s
} else if len(args) > 1 { // default value
return args[1]
}
return ""
}
var resolve func(map[string]interface{}) error
resolve = func(sub map[string]interface{}) error {
for k, v := range sub {
switch vt := v.(type) {
case string:
sub[k] = expand(vt, mapper)
case map[string]interface{}:
if err := resolve(vt); err != nil {
return err
}
case []interface{}:
for i, iface := range vt {
switch it := iface.(type) {
case string:
vt[i] = expand(it, mapper)
case map[string]interface{}:
if err := resolve(it); err != nil {
return err
}
}
}
sub[k] = vt
}
}
return nil
}
return resolve(input)
}
func expand(s string, mapping func(string) string) string {
r := regexp.MustCompile(`\${(.*?)}`)
re := r.FindAllStringSubmatch(s, -1)
for _, i := range re {
if len(i) == 2 { //nolint:gomnd
s = strings.ReplaceAll(s, i[0], mapping(i[1]))
}
}
return s
}

@ -1,228 +0,0 @@
package config
import (
"reflect"
"strings"
"testing"
)
func TestDefaultDecoder(t *testing.T) {
src := &KeyValue{
Key: "service",
Value: []byte("config"),
Format: "",
}
target := make(map[string]interface{})
err := defaultDecoder(src, target)
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(target, map[string]interface{}{"service": []byte("config")}) {
t.Fatal(`target is not equal to map[string]interface{}{"service": "config"}`)
}
src = &KeyValue{
Key: "service.name.alias",
Value: []byte("2233"),
Format: "",
}
target = make(map[string]interface{})
err = defaultDecoder(src, target)
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(map[string]interface{}{
"service": map[string]interface{}{
"name": map[string]interface{}{
"alias": []byte("2233"),
},
},
}, target) {
t.Fatal(`target is not equal to map[string]interface{}{"service": map[string]interface{}{"name": map[string]interface{}{"alias": []byte("2233")}}}`)
}
}
func TestDefaultResolver(t *testing.T) {
var (
portString = "8080"
countInt = 10
rateFloat = 0.9
)
data := map[string]interface{}{
"foo": map[string]interface{}{
"bar": map[string]interface{}{
"notexist": "${NOTEXIST:100}",
"port": "${PORT:8081}",
"count": "${COUNT:0}",
"enable": "${ENABLE:false}",
"rate": "${RATE}",
"empty": "${EMPTY:foobar}",
"url": "${URL:http://example.com}",
"array": []interface{}{
"${PORT}",
map[string]interface{}{"foobar": "${NOTEXIST:8081}"},
},
"value1": "${test.value}",
"value2": "$PORT",
"value3": "abc${PORT}foo${COUNT}bar",
"value4": "${foo${bar}}",
},
},
"test": map[string]interface{}{
"value": "foobar",
},
"PORT": "8080",
"COUNT": "10",
"ENABLE": "true",
"RATE": "0.9",
"EMPTY": "",
}
tests := []struct {
name string
path string
expect interface{}
}{
{
name: "test not exist int env with default",
path: "foo.bar.notexist",
expect: 100,
},
{
name: "test string with default",
path: "foo.bar.port",
expect: portString,
},
{
name: "test int with default",
path: "foo.bar.count",
expect: countInt,
},
{
name: "test bool with default",
path: "foo.bar.enable",
expect: true,
},
{
name: "test float without default",
path: "foo.bar.rate",
expect: rateFloat,
},
{
name: "test empty value with default",
path: "foo.bar.empty",
expect: "",
},
{
name: "test url with default",
path: "foo.bar.url",
expect: "http://example.com",
},
{
name: "test array",
path: "foo.bar.array",
expect: []interface{}{portString, map[string]interface{}{"foobar": "8081"}},
},
{
name: "test ${test.value}",
path: "foo.bar.value1",
expect: "foobar",
},
{
name: "test $PORT",
path: "foo.bar.value2",
expect: "$PORT",
},
{
name: "test abc${PORT}foo${COUNT}bar",
path: "foo.bar.value3",
expect: "abc8080foo10bar",
},
{
name: "test ${foo${bar}}",
path: "foo.bar.value4",
expect: "}",
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
err := defaultResolver(data)
if err != nil {
t.Fatal(err)
}
rd := reader{
values: data,
}
if v, ok := rd.Value(test.path); ok {
var actual interface{}
switch test.expect.(type) {
case int:
if actual, err = v.Int(); err == nil {
if !reflect.DeepEqual(test.expect.(int), int(actual.(int64))) {
t.Fatal("expect is not equal to actual")
}
}
case string:
if actual, err = v.String(); err == nil {
if !reflect.DeepEqual(test.expect, actual) {
t.Fatal("expect is not equal to actual")
}
}
case bool:
if actual, err = v.Bool(); err == nil {
if !reflect.DeepEqual(test.expect, actual) {
t.Fatal("expect is not equal to actual")
}
}
case float64:
if actual, err = v.Float(); err == nil {
if !reflect.DeepEqual(test.expect, actual) {
t.Fatal("expect is not equal to actual")
}
}
default:
actual = v.Load()
if !reflect.DeepEqual(test.expect, actual) {
t.Logf("expect: %#v, actural: %#v", test.expect, actual)
t.Fail()
}
}
if err != nil {
t.Error(err)
}
} else {
t.Error("value path not found")
}
})
}
}
func TestExpand(t *testing.T) {
tests := []struct {
input string
mapping func(string) string
want string
}{
{
input: "${a}",
mapping: func(s string) string {
return strings.ToUpper(s)
},
want: "A",
},
{
input: "a",
mapping: func(s string) string {
return strings.ToUpper(s)
},
want: "a",
},
}
for _, tt := range tests {
if got := expand(tt.input, tt.mapping); got != tt.want {
t.Errorf("expand() want: %s, got: %s", tt.want, got)
}
}
}

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save