Compare commits

..

1 Commits
main ... broker

Author SHA1 Message Date
baozhecheng 3cd4f8126f 暂存 2 years ago
  1. 0
      .fleet/settings.json
  2. 8
      .github/pull_request_template.md
  3. 12
      .github/stable.yml
  4. 21
      .github/workflows/go.yml
  5. 16
      .github/workflows/issue-translator.yml
  6. 2
      .golangci.yml
  7. 30
      Makefile
  8. 2
      README.md
  9. 8
      SECURITY.md
  10. 4
      api/metadata/server.go
  11. 37
      app.go
  12. 20
      app_test.go
  13. 10
      cmd/kratos/go.mod
  14. 38
      cmd/kratos/go.sum
  15. 6
      cmd/kratos/internal/base/mod_test.go
  16. 23
      cmd/kratos/internal/base/path.go
  17. 7
      cmd/kratos/internal/base/repo.go
  18. 2
      cmd/kratos/internal/change/change.go
  19. 10
      cmd/kratos/internal/project/add.go
  20. 16
      cmd/kratos/internal/project/new.go
  21. 70
      cmd/kratos/internal/project/project.go
  22. 29
      cmd/kratos/internal/project/project_linux_test.go
  23. 144
      cmd/kratos/internal/project/project_test.go
  24. 30
      cmd/kratos/internal/project/project_windows_test.go
  25. 7
      cmd/kratos/internal/proto/add/add.go
  26. 6
      cmd/kratos/internal/proto/add/proto.go
  27. 6
      cmd/kratos/internal/proto/client/client.go
  28. 4
      cmd/kratos/internal/proto/proto.go
  29. 18
      cmd/kratos/internal/proto/server/server.go
  30. 40
      cmd/kratos/internal/proto/server/server_test.go
  31. 18
      cmd/kratos/internal/run/run.go
  32. 6
      cmd/kratos/internal/upgrade/upgrade.go
  33. 4
      cmd/kratos/main.go
  34. 2
      cmd/kratos/version.go
  35. 316
      cmd/proto-gen-go-broker/broker.go
  36. 113
      cmd/proto-gen-go-broker/broker/broker.pb.go
  37. 17
      cmd/proto-gen-go-broker/broker/broker.proto
  38. 87
      cmd/proto-gen-go-broker/broker_test.go
  39. 8
      cmd/proto-gen-go-broker/go.mod
  40. 34
      cmd/proto-gen-go-broker/main.go
  41. 63
      cmd/proto-gen-go-broker/template.go
  42. 4
      cmd/proto-gen-go-broker/version.go
  43. 2
      cmd/protoc-gen-go-errors/errors.go
  44. 17
      cmd/protoc-gen-go-errors/errorsTemplate.tpl
  45. 2
      cmd/protoc-gen-go-errors/go.mod
  46. 24
      cmd/protoc-gen-go-errors/go.sum
  47. 22
      cmd/protoc-gen-go-errors/template.go
  48. 2
      cmd/protoc-gen-go-errors/version.go
  49. 29
      cmd/protoc-gen-go-http/http.go
  50. 7
      cmd/protoc-gen-go-http/main.go
  51. 96
      cmd/protoc-gen-go-http/template.go
  52. 2
      cmd/protoc-gen-go-http/version.go
  53. 14
      config/config.go
  54. 41
      config/config_test.go
  55. 6
      config/env/env.go
  56. 4
      config/env/watcher.go
  57. 14
      config/file/file_test.go
  58. 4
      config/file/format_test.go
  59. 4
      config/file/watcher.go
  60. 2
      config/options.go
  61. 14
      config/options_test.go
  62. 20
      config/reader.go
  63. 46
      config/reader_test.go
  64. 64
      config/value.go
  65. 71
      config/value_test.go
  66. 1
      contrib/config/apollo/README.md
  67. 60
      contrib/config/apollo/apollo.go
  68. 90
      contrib/config/apollo/apollo_test.go
  69. 9
      contrib/config/apollo/go.mod
  70. 992
      contrib/config/apollo/go.sum
  71. 9
      contrib/config/apollo/watcher.go
  72. 11
      contrib/config/consul/README.md
  73. 9
      contrib/config/consul/go.mod
  74. 1473
      contrib/config/consul/go.sum
  75. 2
      contrib/config/consul/watcher.go
  76. 5
      contrib/config/etcd/README.md
  77. 6
      contrib/config/etcd/go.mod
  78. 1460
      contrib/config/etcd/go.sum
  79. 4
      contrib/config/kubernetes/config_test.go
  80. 8
      contrib/config/kubernetes/go.mod
  81. 1229
      contrib/config/kubernetes/go.sum
  82. 3
      contrib/config/nacos/README.md
  83. 3
      contrib/config/nacos/go.mod
  84. 1462
      contrib/config/nacos/go.sum
  85. 29
      contrib/config/polaris/README.md
  86. 4
      contrib/config/polaris/config.go
  87. 170
      contrib/config/polaris/config_test.go
  88. 6
      contrib/config/polaris/go.mod
  89. 1191
      contrib/config/polaris/go.sum
  90. 10
      contrib/config/polaris/watcher.go
  91. 2
      contrib/encoding/msgpack/go.mod
  92. 1460
      contrib/encoding/msgpack/go.sum
  93. 21
      contrib/log/aliyun/aliyun_test.go
  94. 6
      contrib/log/aliyun/go.mod
  95. 1432
      contrib/log/aliyun/go.sum
  96. 3
      contrib/log/fluent/go.mod
  97. 1442
      contrib/log/fluent/go.sum
  98. 2
      contrib/log/logrus/go.mod
  99. 1459
      contrib/log/logrus/go.sum
  100. 4
      contrib/log/tencent/go.mod
  101. Some files were not shown because too many files have changed in this diff Show More

@ -3,20 +3,22 @@
1. If this is your first time contributing to Kratos, please read our contribution guide: https://go-kratos.dev/en/docs/community/contribution/ 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. 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 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 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. 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 发送 PR 请求!以下是一些提示:
如果这是你第一次为 Kratos 贡献,请阅读我们的贡献指南:https://go-kratos.dev/en/docs/community/contribution/ 如果这是你第一次为 Kratos 贡献,请阅读我们的贡献指南:https://go-kratos.dev/en/docs/community/contribution/
2、确保您已经为您的 PR 添加或运行了适当的测试和lint,请在提交PR之前使用“make lint”和“make test”,使用“make clean”整理您的 go.mod。 2、确保您已经为您的 PR 添加或运行了适当的测试和lint,请在提交PR之前使用“make lint”和“make test”,使用“make clean”整理您的 go.mod。
3、如果 PR 未完成,您可能需要将其标记为 WIP(Work In Progress)PR 或 Draft PR 3、如果请购单未完成,您可能需要将其标记为 WIP(在制品)PR 或 draft PR
4、请使用语义提交格式标题,如“<类型>[可选范围]:<说明>`,请参阅:https://go-kratos.dev/docs/community/contribution#type 4、请使用语义提交格式标题,如“<类型>[可选范围]:<说明>`,请参阅:https://go-kratos.dev/docs/community/contribution#type
5. 同时请注意,同类的工作请尽量在一个PR中提交,以减轻 review 者的工作负担,不要把一项工作拆分成很多个PR,除非它应该这样做。 5. 同时请注意,同类的工作请尽量在一个PR中提交,以减轻 review 者的工作负担,不要把一项工作拆分成很多个PR,除非它应该这样做。
--> -->
<!--
-->
#### Description (what this PR does / why we need it): #### Description (what this PR does / why we need it):
<!-- <!--

@ -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

@ -13,7 +13,7 @@ jobs:
build: build:
strategy: strategy:
matrix: matrix:
go: [1.19,1.20.x] go: [1.16, 1.17, 1.18, 1.19]
name: build & test name: build & test
runs-on: ubuntu-latest runs-on: ubuntu-latest
services: services:
@ -36,7 +36,7 @@ jobs:
- "8848:8848" - "8848:8848"
- "9848:9848" - "9848:9848"
polaris: polaris:
image: polarismesh/polaris-standalone:latest image: polarismesh/polaris-server-standalone:v1.9.0
ports: ports:
- 8090:8090 - 8090:8090
- 8091:8091 - 8091:8091
@ -44,25 +44,10 @@ jobs:
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- name: Set up Go - name: Set up Go
uses: actions/setup-go@v4.0.1 uses: actions/setup-go@v3.3.1
with: with:
go-version: ${{ matrix.go }} go-version: ${{ matrix.go }}
- name: Setup Environment
run: |
echo "GOPATH=$(go env GOPATH)" >> $GITHUB_ENV
echo "$(go env GOPATH)/bin" >> $GITHUB_PATH
- name: Module cache
uses: actions/cache@v3
with:
path: |
~/.cache/go-build
~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go
- name: Build - name: Build
run: go build ./... run: 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 }}

@ -68,4 +68,4 @@ linters-settings:
# recommend 10-20 # recommend 10-20
min-complexity: 50 min-complexity: 50
goimports: goimports:
local-prefixes: github.com/go-kratos # Put imports beginning with prefix after 3rd-party packages local-prefixes: github.com/go-kratos/kratos # Put imports beginning with prefix after 3rd-party packages

@ -1,27 +1,9 @@
user := $(shell whoami) user := $(shell whoami)
rev := $(shell git rev-parse --short HEAD) rev := $(shell git rev-parse --short HEAD)
os := $(shell expr substr $(shell uname -s) 1 5)
# GOBIN > GOPATH > INSTALLDIR # GOBIN > GOPATH > INSTALLDIR
# Mac OS X
ifeq ($(shell uname),Darwin)
GOBIN := $(shell echo ${GOBIN} | cut -d':' -f1) GOBIN := $(shell echo ${GOBIN} | cut -d':' -f1)
GOPATH := $(shell echo $(GOPATH) | 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 := "" BIN := ""
TOOLS_SHELL="./hack/tools.sh" TOOLS_SHELL="./hack/tools.sh"
@ -32,7 +14,7 @@ LINTER := bin/golangci-lint
ifneq ($(GOBIN),) ifneq ($(GOBIN),)
BIN=$(GOBIN) BIN=$(GOBIN)
else else
# check GOPATH # check GOPATH
ifneq ($(GOPATH),) ifneq ($(GOPATH),)
BIN=$(GOPATH)/bin BIN=$(GOPATH)/bin
endif endif
@ -55,9 +37,9 @@ ifeq ($(user),root)
@cp ./cmd/protoc-gen-go-http/protoc-gen-go-http /usr/bin @cp ./cmd/protoc-gen-go-http/protoc-gen-go-http /usr/bin
else else
#!root, install for current user #!root, install for current user
$(shell if [ -z '$(BIN)' ]; then read -p "Please select installdir: " REPLY; mkdir -p $${REPLY};\ $(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 $${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) 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 endif
@which protoc-gen-go &> /dev/null || go get google.golang.org/protobuf/cmd/protoc-gen-go @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-go-grpc &> /dev/null || go get google.golang.org/grpc/cmd/protoc-gen-go-grpc
@ -89,7 +71,7 @@ test:
.PHONY: test-coverage .PHONY: test-coverage
test-coverage: test-coverage:
@${TOOLS_SHELL} test_coverage @${TOOLS_SHELL} test_coverage
@echo "go test with coverage finished" @echo "go test with coverage finished"
.PHONY: lint .PHONY: lint
lint: $(LINTER) lint: $(LINTER)
@ -99,4 +81,4 @@ lint: $(LINTER)
.PHONY: proto .PHONY: proto
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=./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 protoc --proto_path=./third_party --go_out=paths=source_relative:./ errors/errors.proto

@ -110,5 +110,5 @@ 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. - [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. - [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. - [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. - [tal-tech/go-zero](https://github.com/tal-tech/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. - [beego/beego](https://github.com/beego/beego) is a web framework including RESTful APIs, web apps and backend services.

@ -5,10 +5,10 @@
Use this section to tell people about which versions of your project are Use this section to tell people about which versions of your project are
currently being supported with security updates. currently being supported with security updates.
| Version | Supported | | Version | Supported |
|-------------|--------------------| | ------- | ------------------ |
| 2.0.rc1 | :white_check_mark: | | 2.0.rc1 | :white_check_mark: |
| < 2.0.beta3 | :x: | | < 2.0.beta3 | :x: |
## Reporting a Vulnerability ## Reporting a Vulnerability

@ -98,7 +98,7 @@ func (s *Server) load() error {
} }
// ListServices return all services // ListServices return all services
func (s *Server) ListServices(_ context.Context, _ *ListServicesRequest) (*ListServicesReply, error) { func (s *Server) ListServices(ctx context.Context, in *ListServicesRequest) (*ListServicesReply, error) {
s.lock.Lock() s.lock.Lock()
defer s.lock.Unlock() defer s.lock.Unlock()
if err := s.load(); err != nil { if err := s.load(); err != nil {
@ -122,7 +122,7 @@ func (s *Server) ListServices(_ context.Context, _ *ListServicesRequest) (*ListS
} }
// GetServiceDesc return service meta by name // GetServiceDesc return service meta by name
func (s *Server) GetServiceDesc(_ context.Context, in *GetServiceDescRequest) (*GetServiceDescReply, error) { func (s *Server) GetServiceDesc(ctx context.Context, in *GetServiceDescRequest) (*GetServiceDescReply, error) {
s.lock.Lock() s.lock.Lock()
defer s.lock.Unlock() defer s.lock.Unlock()
if err := s.load(); err != nil { if err := s.load(); err != nil {

@ -89,15 +89,8 @@ func (a *App) Run() error {
a.mu.Lock() a.mu.Lock()
a.instance = instance a.instance = instance
a.mu.Unlock() a.mu.Unlock()
sctx := NewContext(a.ctx, a) eg, ctx := errgroup.WithContext(NewContext(a.ctx, a))
eg, ctx := errgroup.WithContext(sctx)
wg := sync.WaitGroup{} wg := sync.WaitGroup{}
for _, fn := range a.opts.beforeStart {
if err = fn(sctx); err != nil {
return err
}
}
for _, srv := range a.opts.servers { for _, srv := range a.opts.servers {
srv := srv srv := srv
eg.Go(func() error { eg.Go(func() error {
@ -109,23 +102,17 @@ func (a *App) Run() error {
wg.Add(1) wg.Add(1)
eg.Go(func() error { eg.Go(func() error {
wg.Done() // here is to ensure server start has begun running before register, so defer is not needed wg.Done() // here is to ensure server start has begun running before register, so defer is not needed
return srv.Start(sctx) return srv.Start(NewContext(a.opts.ctx, a))
}) })
} }
wg.Wait() wg.Wait()
if a.opts.registrar != nil { if a.opts.registrar != nil {
rctx, rcancel := context.WithTimeout(ctx, a.opts.registrarTimeout) rctx, rcancel := context.WithTimeout(ctx, a.opts.registrarTimeout)
defer rcancel() defer rcancel()
if err = a.opts.registrar.Register(rctx, instance); err != nil { 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 return err
} }
} }
c := make(chan os.Signal, 1) c := make(chan os.Signal, 1)
signal.Notify(c, a.opts.sigs...) signal.Notify(c, a.opts.sigs...)
eg.Go(func() error { eg.Go(func() error {
@ -136,36 +123,28 @@ func (a *App) Run() error {
return a.Stop() return a.Stop()
} }
}) })
if err = eg.Wait(); err != nil && !errors.Is(err, context.Canceled) { if err := eg.Wait(); err != nil && !errors.Is(err, context.Canceled) {
return err return err
} }
for _, fn := range a.opts.afterStop { return nil
err = fn(sctx)
}
return err
} }
// Stop gracefully stops the application. // Stop gracefully stops the application.
func (a *App) Stop() (err error) { func (a *App) Stop() error {
sctx := NewContext(a.ctx, a)
for _, fn := range a.opts.beforeStop {
err = fn(sctx)
}
a.mu.Lock() a.mu.Lock()
instance := a.instance instance := a.instance
a.mu.Unlock() a.mu.Unlock()
if a.opts.registrar != nil && instance != nil { if a.opts.registrar != nil && instance != nil {
ctx, cancel := context.WithTimeout(NewContext(a.ctx, a), a.opts.registrarTimeout) ctx, cancel := context.WithTimeout(NewContext(a.ctx, a), a.opts.registrarTimeout)
defer cancel() defer cancel()
if err = a.opts.registrar.Deregister(ctx, instance); err != nil { if err := a.opts.registrar.Deregister(ctx, instance); err != nil {
return err return err
} }
} }
if a.cancel != nil { if a.cancel != nil {
a.cancel() a.cancel()
} }
return err return nil
} }
func (a *App) buildInstance() (*registry.ServiceInstance, error) { func (a *App) buildInstance() (*registry.ServiceInstance, error) {

@ -19,7 +19,7 @@ type mockRegistry struct {
service map[string]*registry.ServiceInstance service map[string]*registry.ServiceInstance
} }
func (r *mockRegistry) Register(_ context.Context, service *registry.ServiceInstance) error { func (r *mockRegistry) Register(ctx context.Context, service *registry.ServiceInstance) error {
if service == nil || service.ID == "" { if service == nil || service.ID == "" {
return errors.New("no service id") return errors.New("no service id")
} }
@ -30,7 +30,7 @@ func (r *mockRegistry) Register(_ context.Context, service *registry.ServiceInst
} }
// Deregister the registration. // Deregister the registration.
func (r *mockRegistry) Deregister(_ context.Context, service *registry.ServiceInstance) error { func (r *mockRegistry) Deregister(ctx context.Context, service *registry.ServiceInstance) error {
r.lk.Lock() r.lk.Lock()
defer r.lk.Unlock() defer r.lk.Unlock()
if r.service[service.ID] == nil { if r.service[service.ID] == nil {
@ -47,22 +47,6 @@ func TestApp(t *testing.T) {
Name("kratos"), Name("kratos"),
Version("v1.0.0"), Version("v1.0.0"),
Server(hs, gs), 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)}), Registrar(&mockRegistry{service: make(map[string]*registry.ServiceInstance)}),
) )
time.AfterFunc(time.Second, func() { time.AfterFunc(time.Second, func() {

@ -3,13 +3,15 @@ module github.com/go-kratos/kratos/cmd/kratos/v2
go 1.16 go 1.16
require ( require (
github.com/AlecAivazis/survey/v2 v2.3.7 github.com/AlecAivazis/survey/v2 v2.3.4
github.com/emicklei/proto v1.10.0 github.com/emicklei/proto v1.10.0
github.com/fatih/color v1.13.0 github.com/fatih/color v1.13.0
github.com/mattn/go-colorable v0.1.12 // indirect github.com/mattn/go-colorable v0.1.12 // indirect
github.com/spf13/cobra v1.4.0 github.com/spf13/cobra v1.4.0
github.com/stretchr/testify v1.7.0 // indirect github.com/stretchr/testify v1.7.0 // indirect
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 golang.org/x/mod v0.5.1
golang.org/x/text v0.4.0 golang.org/x/sys v0.0.0-20211205182925-97ca703d548d // indirect
gopkg.in/yaml.v3 v3.0.0 // indirect golang.org/x/text v0.3.7
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
) )

@ -1,5 +1,5 @@
github.com/AlecAivazis/survey/v2 v2.3.7 h1:6I/u8FvytdGsgonrYsVn2t8t4QiRnh6QSTqkkhIiSjQ= github.com/AlecAivazis/survey/v2 v2.3.4 h1:pchTU9rsLUSvWEl2Aq9Pv3k0IE2fkqtGxazskAMd9Ng=
github.com/AlecAivazis/survey/v2 v2.3.7/go.mod h1:xUTIdE4KCOIjsBAE1JYsUPoCqYdZ1reCfTwbto0Fduo= github.com/AlecAivazis/survey/v2 v2.3.4/go.mod h1:hrV6Y/kQCLhIZXGcriDCUBtB3wnN7156gMXJ3+b23xM=
github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2 h1:+vx7roKuyA63nhn5WAunQHLTznkw5W8b1Xc0dNjp83s= 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/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/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
@ -39,41 +39,37 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 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 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 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-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/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s= golang.org/x/mod v0.5.1 h1:OJxoQ/rynoF0dcCdI7cLPktw/hR2cueqYfjm43oqK38=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-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-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-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-20190222072716-a9d3bda3a223/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-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 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-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-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-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-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-20211205182925-97ca703d548d h1:FjkYO/PPp4Wi0EAUOVLxePm7qVW4r4ctbWpURyuOD0E=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f h1:v4INt8xihDGvnrfjMDVXGxw9wrfxYyCjk0KbXjhR55s= golang.org/x/sys v0.0.0-20211205182925-97ca703d548d/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-20210503060354-a79de5458b56 h1:b8jxX3zqjpqb2LklXPzKSGJhzyxCOZSz8ncv8Nv+y7w=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210503060354-a79de5458b56/go.mod h1:tfny5GFUkzUvx4ps4ajbZsCe5lw1metzhBm9T3x7oIY=
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.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.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 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-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.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-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 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.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-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-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

@ -18,6 +18,8 @@ func TestModulePath(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
defer os.RemoveAll("/tmp/test_mod")
f, err := os.Create("/tmp/test_mod/go.mod") f, err := os.Create("/tmp/test_mod/go.mod")
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
@ -36,8 +38,6 @@ go 1.16`
t.Fatal(err) t.Fatal(err)
} }
if p != "github.com/go-kratos/kratos/v2" { if p != "github.com/go-kratos/kratos/v2" {
t.Fatalf("want: %s, got: %s", "github.com/go-kratos/kratos/v2", p) t.Fatalf("want: %s, got: %s", "module github.com/go-kratos/kratos/v2", p)
} }
t.Cleanup(func() { os.RemoveAll("/tmp/test_mod") })
} }

@ -5,6 +5,7 @@ import (
"fmt" "fmt"
"log" "log"
"os" "os"
"path"
"path/filepath" "path/filepath"
"strings" "strings"
@ -16,7 +17,7 @@ func kratosHome() string {
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
home := filepath.Join(dir, ".kratos") home := path.Join(dir, ".kratos")
if _, err := os.Stat(home); os.IsNotExist(err) { if _, err := os.Stat(home); os.IsNotExist(err) {
if err := os.MkdirAll(home, 0o700); err != nil { if err := os.MkdirAll(home, 0o700); err != nil {
log.Fatal(err) log.Fatal(err)
@ -26,7 +27,7 @@ func kratosHome() string {
} }
func kratosHomeWithDir(dir string) string { func kratosHomeWithDir(dir string) string {
home := filepath.Join(kratosHome(), dir) home := path.Join(kratosHome(), dir)
if _, err := os.Stat(home); os.IsNotExist(err) { if _, err := os.Stat(home); os.IsNotExist(err) {
if err := os.MkdirAll(home, 0o700); err != nil { if err := os.MkdirAll(home, 0o700); err != nil {
log.Fatal(err) log.Fatal(err)
@ -36,6 +37,7 @@ func kratosHomeWithDir(dir string) string {
} }
func copyFile(src, dst string, replaces []string) error { func copyFile(src, dst string, replaces []string) error {
var err error
srcinfo, err := os.Stat(src) srcinfo, err := os.Stat(src)
if err != nil { if err != nil {
return err return err
@ -56,26 +58,27 @@ func copyFile(src, dst string, replaces []string) error {
} }
func copyDir(src, dst string, replaces, ignores []string) error { func copyDir(src, dst string, replaces, ignores []string) error {
srcinfo, err := os.Stat(src) var err error
if err != nil { var fds []os.DirEntry
var srcinfo os.FileInfo
if srcinfo, err = os.Stat(src); err != nil {
return err return err
} }
err = os.MkdirAll(dst, srcinfo.Mode()) if err = os.MkdirAll(dst, srcinfo.Mode()); err != nil {
if err != nil {
return err return err
} }
fds, err := os.ReadDir(src) if fds, err = os.ReadDir(src); err != nil {
if err != nil {
return err return err
} }
for _, fd := range fds { for _, fd := range fds {
if hasSets(fd.Name(), ignores) { if hasSets(fd.Name(), ignores) {
continue continue
} }
srcfp := filepath.Join(src, fd.Name()) srcfp := path.Join(src, fd.Name())
dstfp := filepath.Join(dst, fd.Name()) dstfp := path.Join(dst, fd.Name())
var e error var e error
if fd.IsDir() { if fd.IsDir() {
e = copyDir(srcfp, dstfp, replaces, ignores) e = copyDir(srcfp, dstfp, replaces, ignores)

@ -7,7 +7,6 @@ import (
"os" "os"
"os/exec" "os/exec"
"path" "path"
"path/filepath"
"strings" "strings"
) )
@ -69,7 +68,7 @@ func (r *Repo) Pull(ctx context.Context) error {
cmd.Dir = r.Path() cmd.Dir = r.Path()
_, err := cmd.CombinedOutput() _, err := cmd.CombinedOutput()
if err != nil { if err != nil {
return err return nil
} }
cmd = exec.CommandContext(ctx, "git", "pull") cmd = exec.CommandContext(ctx, "git", "pull")
cmd.Dir = r.Path() cmd.Dir = r.Path()
@ -105,7 +104,7 @@ func (r *Repo) CopyTo(ctx context.Context, to string, modPath string, ignores []
if err := r.Clone(ctx); err != nil { if err := r.Clone(ctx); err != nil {
return err return err
} }
mod, err := ModulePath(filepath.Join(r.Path(), "go.mod")) mod, err := ModulePath(path.Join(r.Path(), "go.mod"))
if err != nil { if err != nil {
return err return err
} }
@ -117,7 +116,7 @@ func (r *Repo) CopyToV2(ctx context.Context, to string, modPath string, ignores,
if err := r.Clone(ctx); err != nil { if err := r.Clone(ctx); err != nil {
return err return err
} }
mod, err := ModulePath(filepath.Join(r.Path(), "go.mod")) mod, err := ModulePath(path.Join(r.Path(), "go.mod"))
if err != nil { if err != nil {
return err return err
} }

@ -28,7 +28,7 @@ func init() {
token = os.Getenv("GITHUB_TOKEN") token = os.Getenv("GITHUB_TOKEN")
} }
func run(_ *cobra.Command, args []string) { func run(cmd *cobra.Command, args []string) {
owner, repo := ParseGithubURL(repoURL) owner, repo := ParseGithubURL(repoURL)
api := GithubAPI{Owner: owner, Repo: repo, Token: token} api := GithubAPI{Owner: owner, Repo: repo, Token: token}
version := "latest" version := "latest"

@ -4,7 +4,7 @@ import (
"context" "context"
"fmt" "fmt"
"os" "os"
"path/filepath" "path"
"github.com/AlecAivazis/survey/v2" "github.com/AlecAivazis/survey/v2"
"github.com/fatih/color" "github.com/fatih/color"
@ -17,7 +17,7 @@ var repoAddIgnores = []string{
} }
func (p *Project) Add(ctx context.Context, dir string, layout string, branch string, mod string) error { func (p *Project) Add(ctx context.Context, dir string, layout string, branch string, mod string) error {
to := filepath.Join(dir, p.Name) to := path.Join(dir, p.Path)
if _, err := os.Stat(to); !os.IsNotExist(err) { if _, err := os.Stat(to); !os.IsNotExist(err) {
fmt.Printf("🚫 %s already exists\n", p.Name) fmt.Printf("🚫 %s already exists\n", p.Name)
@ -40,13 +40,13 @@ func (p *Project) Add(ctx context.Context, dir string, layout string, branch str
repo := base.NewRepo(layout, branch) 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 { if err := repo.CopyToV2(ctx, to, path.Join(mod, p.Path), repoAddIgnores, []string{path.Join(p.Path, "api"), "api"}); err != nil {
return err return err
} }
e := os.Rename( e := os.Rename(
filepath.Join(to, "cmd", "server"), path.Join(to, "cmd", "server"),
filepath.Join(to, "cmd", p.Name), path.Join(to, "cmd", p.Name),
) )
if e != nil { if e != nil {
return e return e

@ -4,12 +4,12 @@ import (
"context" "context"
"fmt" "fmt"
"os" "os"
"path/filepath" "path"
"github.com/go-kratos/kratos/cmd/kratos/v2/internal/base"
"github.com/AlecAivazis/survey/v2" "github.com/AlecAivazis/survey/v2"
"github.com/fatih/color" "github.com/fatih/color"
"github.com/go-kratos/kratos/cmd/kratos/v2/internal/base"
) )
// Project is a project template. // Project is a project template.
@ -20,14 +20,14 @@ type Project struct {
// New new a project from remote repo. // New new a project from remote repo.
func (p *Project) New(ctx context.Context, dir string, layout string, branch string) error { func (p *Project) New(ctx context.Context, dir string, layout string, branch string) error {
to := filepath.Join(dir, p.Name) to := path.Join(dir, p.Name)
if _, err := os.Stat(to); !os.IsNotExist(err) { if _, err := os.Stat(to); !os.IsNotExist(err) {
fmt.Printf("🚫 %s already exists\n", p.Name) fmt.Printf("🚫 %s already exists\n", p.Name)
override := false
prompt := &survey.Confirm{ prompt := &survey.Confirm{
Message: "📂 Do you want to override the folder ?", Message: "📂 Do you want to override the folder ?",
Help: "Delete the existing folder and create the project.", Help: "Delete the existing folder and create the project.",
} }
var override bool
e := survey.AskOne(prompt, &override) e := survey.AskOne(prompt, &override)
if e != nil { if e != nil {
return e return e
@ -39,12 +39,12 @@ func (p *Project) New(ctx context.Context, dir string, layout string, branch str
} }
fmt.Printf("🚀 Creating service %s, layout repo is %s, please wait a moment.\n\n", p.Name, layout) fmt.Printf("🚀 Creating service %s, layout repo is %s, please wait a moment.\n\n", p.Name, layout)
repo := base.NewRepo(layout, branch) repo := base.NewRepo(layout, branch)
if err := repo.CopyTo(ctx, to, p.Name, []string{".git", ".github"}); err != nil { if err := repo.CopyTo(ctx, to, p.Path, []string{".git", ".github"}); err != nil {
return err return err
} }
e := os.Rename( e := os.Rename(
filepath.Join(to, "cmd", "server"), path.Join(to, "cmd", "server"),
filepath.Join(to, "cmd", p.Name), path.Join(to, "cmd", p.Name),
) )
if e != nil { if e != nil {
return e return e

@ -5,8 +5,7 @@ import (
"errors" "errors"
"fmt" "fmt"
"os" "os"
"path/filepath" "path"
"strings"
"time" "time"
"github.com/AlecAivazis/survey/v2" "github.com/AlecAivazis/survey/v2"
@ -41,7 +40,7 @@ func init() {
CmdNew.Flags().BoolVarP(&nomod, "nomod", "", nomod, "retain go mod") CmdNew.Flags().BoolVarP(&nomod, "nomod", "", nomod, "retain go mod")
} }
func run(_ *cobra.Command, args []string) { func run(cmd *cobra.Command, args []string) {
wd, err := os.Getwd() wd, err := os.Getwd()
if err != nil { if err != nil {
panic(err) panic(err)
@ -65,34 +64,23 @@ func run(_ *cobra.Command, args []string) {
} else { } else {
name = args[0] name = args[0]
} }
projectName, workingDir := processProjectParams(name, wd) p := &Project{Name: path.Base(name), Path: name}
p := &Project{Name: projectName}
done := make(chan error, 1) done := make(chan error, 1)
go func() { go func() {
if !nomod { if !nomod {
done <- p.New(ctx, workingDir, repoURL, branch) done <- p.New(ctx, wd, repoURL, branch)
return return
} }
projectRoot := getgomodProjectRoot(workingDir) if _, e := os.Stat(path.Join(wd, "go.mod")); os.IsNotExist(e) {
if gomodIsNotExistIn(projectRoot) { done <- fmt.Errorf("🚫 go.mod don't exists in %s", wd)
done <- fmt.Errorf("🚫 go.mod don't exists in %s", projectRoot)
return return
} }
p.Path, err = filepath.Rel(projectRoot, filepath.Join(workingDir, projectName)) mod, e := base.ModulePath(path.Join(wd, "go.mod"))
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 { if e != nil {
done <- fmt.Errorf("🚫 failed to parse `go.mod`: %v", e) panic(e)
return
} }
// Get the relative path for adding a project based on Go modules done <- p.Add(ctx, wd, repoURL, branch, mod)
p.Path = filepath.Join(strings.TrimPrefix(workingDir, projectRoot+"/"), p.Name)
done <- p.Add(ctx, workingDir, repoURL, branch, mod)
}() }()
select { select {
case <-ctx.Done(): case <-ctx.Done():
@ -107,43 +95,3 @@ func run(_ *cobra.Command, args []string) {
} }
} }
} }
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)
}
})
}
}

@ -19,11 +19,8 @@ var CmdAdd = &cobra.Command{
Run: run, Run: run,
} }
func run(_ *cobra.Command, args []string) { func run(cmd *cobra.Command, args []string) {
if len(args) == 0 { // kratos proto add helloworld/v1/helloworld.proto
fmt.Println("Please enter the proto file or directory")
return
}
input := args[0] input := args[0]
n := strings.LastIndex(input, "/") n := strings.LastIndex(input, "/")
if n == -1 { if n == -1 {

@ -3,7 +3,7 @@ package add
import ( import (
"fmt" "fmt"
"os" "os"
"path/filepath" "path"
) )
// Proto is a proto generator. // Proto is a proto generator.
@ -26,13 +26,13 @@ func (p *Proto) Generate() error {
if err != nil { if err != nil {
panic(err) panic(err)
} }
to := filepath.Join(wd, p.Path) to := path.Join(wd, p.Path)
if _, err := os.Stat(to); os.IsNotExist(err) { if _, err := os.Stat(to); os.IsNotExist(err) {
if err := os.MkdirAll(to, 0o700); err != nil { if err := os.MkdirAll(to, 0o700); err != nil {
return err return err
} }
} }
name := filepath.Join(to, p.Name) name := path.Join(to, p.Name)
if _, err := os.Stat(name); !os.IsNotExist(err) { if _, err := os.Stat(name); !os.IsNotExist(err) {
return fmt.Errorf("%s already exists", p.Name) return fmt.Errorf("%s already exists", p.Name)
} }

@ -8,9 +8,9 @@ import (
"regexp" "regexp"
"strings" "strings"
"github.com/spf13/cobra"
"github.com/go-kratos/kratos/cmd/kratos/v2/internal/base" "github.com/go-kratos/kratos/cmd/kratos/v2/internal/base"
"github.com/spf13/cobra"
) )
// CmdClient represents the source command. // CmdClient represents the source command.
@ -30,7 +30,7 @@ func init() {
CmdClient.Flags().StringVarP(&protoPath, "proto_path", "p", protoPath, "proto path") CmdClient.Flags().StringVarP(&protoPath, "proto_path", "p", protoPath, "proto path")
} }
func run(_ *cobra.Command, args []string) { func run(cmd *cobra.Command, args []string) {
if len(args) == 0 { if len(args) == 0 {
fmt.Println("Please enter the proto file or directory") fmt.Println("Please enter the proto file or directory")
return return

@ -1,11 +1,11 @@
package proto package proto
import ( 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/add"
"github.com/go-kratos/kratos/cmd/kratos/v2/internal/proto/client" "github.com/go-kratos/kratos/cmd/kratos/v2/internal/proto/client"
"github.com/go-kratos/kratos/cmd/kratos/v2/internal/proto/server" "github.com/go-kratos/kratos/cmd/kratos/v2/internal/proto/server"
"github.com/spf13/cobra"
) )
// CmdProto represents the proto command. // CmdProto represents the proto command.

@ -4,7 +4,7 @@ import (
"fmt" "fmt"
"log" "log"
"os" "os"
"path/filepath" "path"
"strings" "strings"
"github.com/emicklei/proto" "github.com/emicklei/proto"
@ -16,8 +16,8 @@ import (
// CmdServer the service command. // CmdServer the service command.
var CmdServer = &cobra.Command{ var CmdServer = &cobra.Command{
Use: "server", Use: "server",
Short: "Generate the proto server implementations", Short: "Generate the proto Server implementations",
Long: "Generate the proto server implementations. Example: kratos proto server api/xxx.proto --target-dir=internal/service", Long: "Generate the proto Server implementations. Example: kratos proto server api/xxx.proto -target-dir=internal/service",
Run: run, Run: run,
} }
var targetDir string var targetDir string
@ -26,7 +26,7 @@ func init() {
CmdServer.Flags().StringVarP(&targetDir, "target-dir", "t", "internal/service", "generate target directory") CmdServer.Flags().StringVarP(&targetDir, "target-dir", "t", "internal/service", "generate target directory")
} }
func run(_ *cobra.Command, args []string) { func run(cmd *cobra.Command, args []string) {
if len(args) == 0 { if len(args) == 0 {
fmt.Fprintln(os.Stderr, "Please specify the proto file. Example: kratos proto server api/xxx.proto") fmt.Fprintln(os.Stderr, "Please specify the proto file. Example: kratos proto server api/xxx.proto")
return return
@ -64,8 +64,8 @@ func run(_ *cobra.Command, args []string) {
continue continue
} }
cs.Methods = append(cs.Methods, &Method{ cs.Methods = append(cs.Methods, &Method{
Service: serviceName(s.Name), Name: serviceName(r.Name), Request: parametersName(r.RequestType), Service: serviceName(s.Name), Name: serviceName(r.Name), Request: r.RequestType,
Reply: parametersName(r.ReturnsType), Type: getMethodType(r.StreamsRequest, r.StreamsReturns), Reply: r.ReturnsType, Type: getMethodType(r.StreamsRequest, r.StreamsReturns),
}) })
} }
res = append(res, cs) res = append(res, cs)
@ -76,7 +76,7 @@ func run(_ *cobra.Command, args []string) {
return return
} }
for _, s := range res { for _, s := range res {
to := filepath.Join(targetDir, strings.ToLower(s.Service)+".go") to := path.Join(targetDir, strings.ToLower(s.Service)+".go")
if _, err := os.Stat(to); !os.IsNotExist(err) { if _, err := os.Stat(to); !os.IsNotExist(err) {
fmt.Fprintf(os.Stderr, "%s already exists: %s\n", s.Service, to) fmt.Fprintf(os.Stderr, "%s already exists: %s\n", s.Service, to)
continue continue
@ -105,10 +105,6 @@ func getMethodType(streamsRequest, streamsReturns bool) MethodType {
return unaryType return unaryType
} }
func parametersName(name string) string {
return strings.ReplaceAll(name, ".", "_")
}
func serviceName(name string) string { func serviceName(name string) string {
return toUpperCamelCase(strings.Split(name, ".")[0]) return toUpperCamelCase(strings.Split(name, ".")[0])
} }

@ -60,43 +60,3 @@ func Test_serviceName(t *testing.T) {
}) })
} }
} }
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)
}
})
}
}

@ -4,6 +4,7 @@ import (
"fmt" "fmt"
"os" "os"
"os/exec" "os/exec"
"path"
"path/filepath" "path/filepath"
"strings" "strings"
@ -18,11 +19,6 @@ var CmdRun = &cobra.Command{
Long: "Run project. Example: kratos run", Long: "Run project. Example: kratos run",
Run: Run, Run: Run,
} }
var targetDir string
func init() {
CmdRun.Flags().StringVarP(&targetDir, "work", "w", "", "target working directory")
}
// Run run project. // Run run project.
func Run(cmd *cobra.Command, args []string) { func Run(cmd *cobra.Command, args []string) {
@ -68,11 +64,10 @@ func Run(cmd *cobra.Command, args []string) {
dir = cmdPath[dir] dir = cmdPath[dir]
} }
} }
fd := exec.Command("go", append([]string{"run", dir}, programArgs...)...) fd := exec.Command("go", append([]string{"run", "."}, programArgs...)...)
fd.Stdout = os.Stdout fd.Stdout = os.Stdout
fd.Stderr = os.Stderr fd.Stderr = os.Stderr
fd.Dir = dir fd.Dir = dir
changeWorkingDirectory(fd, targetDir)
if err := fd.Run(); err != nil { if err := fd.Run(); err != nil {
fmt.Fprintf(os.Stderr, "\033[31mERROR: %s\033[m\n", err.Error()) fmt.Fprintf(os.Stderr, "\033[31mERROR: %s\033[m\n", err.Error())
return return
@ -107,7 +102,7 @@ func findCMD(base string) (map[string]string, error) {
} }
for _, fileInfo := range paths { for _, fileInfo := range paths {
if fileInfo.IsDir() { if fileInfo.IsDir() {
abs := filepath.Join(walkPath, fileInfo.Name()) abs := path.Join(walkPath, fileInfo.Name())
cmdPath[strings.TrimPrefix(abs, wd)] = abs cmdPath[strings.TrimPrefix(abs, wd)] = abs
} }
} }
@ -136,10 +131,3 @@ func findCMD(base string) (map[string]string, error) {
} }
return map[string]string{"": base}, nil return map[string]string{"": base}, nil
} }
func changeWorkingDirectory(cmd *exec.Cmd, targetDir string) {
targetDir = strings.TrimSpace(targetDir)
if targetDir != "" {
cmd.Dir = targetDir
}
}

@ -3,9 +3,9 @@ package upgrade
import ( import (
"fmt" "fmt"
"github.com/spf13/cobra"
"github.com/go-kratos/kratos/cmd/kratos/v2/internal/base" "github.com/go-kratos/kratos/cmd/kratos/v2/internal/base"
"github.com/spf13/cobra"
) )
// CmdUpgrade represents the upgrade command. // CmdUpgrade represents the upgrade command.
@ -17,7 +17,7 @@ var CmdUpgrade = &cobra.Command{
} }
// Run upgrade the kratos tools. // Run upgrade the kratos tools.
func Run(_ *cobra.Command, _ []string) { func Run(cmd *cobra.Command, args []string) {
err := base.GoInstall( err := base.GoInstall(
"github.com/go-kratos/kratos/cmd/kratos/v2@latest", "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-http/v2@latest",

@ -3,13 +3,13 @@ package main
import ( import (
"log" "log"
"github.com/spf13/cobra"
"github.com/go-kratos/kratos/cmd/kratos/v2/internal/change" "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/project"
"github.com/go-kratos/kratos/cmd/kratos/v2/internal/proto" "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/run"
"github.com/go-kratos/kratos/cmd/kratos/v2/internal/upgrade" "github.com/go-kratos/kratos/cmd/kratos/v2/internal/upgrade"
"github.com/spf13/cobra"
) )
var rootCmd = &cobra.Command{ var rootCmd = &cobra.Command{

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

@ -0,0 +1,316 @@
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) *protogen.GeneratedFile {
if len(file.Services) == 0 || (omitempty && !hasHTTPRule(file.Services)) {
return nil
}
filename := file.GeneratedFilenamePrefix + "_broker.pb.go"
g := gen.NewGeneratedFile(filename, file.GoImportPath)
g.P("// Code generated by protoc-gen-go-broker. DO NOT EDIT.")
g.P("// versions:")
g.P(fmt.Sprintf("// - protoc-gen-go-broker %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)
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) {
if len(file.Services) == 0 {
return
}
for _, service := range file.Services {
genService(gen, file, g, service, omitempty)
}
}
func genService(gen *protogen.Plugin, file *protogen.File, g *protogen.GeneratedFile, service *protogen.Service, omitempty bool) {
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, method, bind))
}
sd.Methods = append(sd.Methods, buildHTTPRule(g, method, rule))
} else if !omitempty {
path := fmt.Sprintf("/%s/%s", 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, m *protogen.Method, rule *annotations.HttpRule) *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
}
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()
}
}
}
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),
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."

@ -0,0 +1,113 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.28.1
// protoc v3.19.4
// source: cmd/proto-gen-go-broker/broker/broker.proto
package broker
import (
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
descriptorpb "google.golang.org/protobuf/types/descriptorpb"
reflect "reflect"
)
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)
)
var file_cmd_proto_gen_go_broker_broker_broker_proto_extTypes = []protoimpl.ExtensionInfo{
{
ExtendedType: (*descriptorpb.MethodOptions)(nil),
ExtensionType: (*string)(nil),
Field: 1001011,
Name: "errors.receive_topic",
Tag: "bytes,1001011,opt,name=receive_topic",
Filename: "cmd/proto-gen-go-broker/broker/broker.proto",
},
{
ExtendedType: (*descriptorpb.MethodOptions)(nil),
ExtensionType: (*string)(nil),
Field: 1008611,
Name: "errors.output_topic",
Tag: "bytes,1008611,opt,name=output_topic",
Filename: "cmd/proto-gen-go-broker/broker/broker.proto",
},
}
// Extension fields to descriptorpb.MethodOptions.
var (
// optional string receive_topic = 1001011;
E_ReceiveTopic = &file_cmd_proto_gen_go_broker_broker_broker_proto_extTypes[0]
// optional string output_topic = 1008611;
E_OutputTopic = &file_cmd_proto_gen_go_broker_broker_broker_proto_extTypes[1]
)
var File_cmd_proto_gen_go_broker_broker_broker_proto protoreflect.FileDescriptor
var file_cmd_proto_gen_go_broker_broker_broker_proto_rawDesc = []byte{
0x0a, 0x2b, 0x63, 0x6d, 0x64, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2d, 0x67, 0x65, 0x6e, 0x2d,
0x67, 0x6f, 0x2d, 0x62, 0x72, 0x6f, 0x6b, 0x65, 0x72, 0x2f, 0x62, 0x72, 0x6f, 0x6b, 0x65, 0x72,
0x2f, 0x62, 0x72, 0x6f, 0x6b, 0x65, 0x72, 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, 0x3a, 0x45, 0x0a, 0x0d, 0x72, 0x65, 0x63, 0x65, 0x69,
0x76, 0x65, 0x5f, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x12, 0x1e, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c,
0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4d, 0x65, 0x74, 0x68, 0x6f,
0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0xb3, 0x8c, 0x3d, 0x20, 0x01, 0x28, 0x09,
0x52, 0x0c, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x54, 0x6f, 0x70, 0x69, 0x63, 0x3a, 0x43,
0x0a, 0x0c, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x5f, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x12, 0x1e,
0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66,
0x2e, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0xe3,
0xc7, 0x3d, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x54, 0x6f,
0x70, 0x69, 0x63, 0x42, 0x6a, 0x0a, 0x18, 0x63, 0x6f, 0x6d, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75,
0x62, 0x2e, 0x6b, 0x72, 0x61, 0x74, 0x6f, 0x73, 0x2e, 0x42, 0x72, 0x6f, 0x6b, 0x65, 0x72, 0x50,
0x01, 0x5a, 0x3d, 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, 0x63, 0x6d, 0x64, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2d, 0x67, 0x65, 0x6e, 0x2d,
0x67, 0x6f, 0x2d, 0x62, 0x72, 0x6f, 0x6b, 0x65, 0x72, 0x3b, 0x62, 0x72, 0x6f, 0x6b, 0x65, 0x72,
0xa2, 0x02, 0x0c, 0x4b, 0x72, 0x61, 0x74, 0x6f, 0x73, 0x42, 0x72, 0x6f, 0x6b, 0x65, 0x72, 0x62,
0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var file_cmd_proto_gen_go_broker_broker_broker_proto_goTypes = []interface{}{
(*descriptorpb.MethodOptions)(nil), // 0: google.protobuf.MethodOptions
}
var file_cmd_proto_gen_go_broker_broker_broker_proto_depIdxs = []int32{
0, // 0: errors.receive_topic:extendee -> google.protobuf.MethodOptions
0, // 1: errors.output_topic:extendee -> google.protobuf.MethodOptions
2, // [2:2] is the sub-list for method output_type
2, // [2:2] is the sub-list for method input_type
2, // [2:2] is the sub-list for extension type_name
0, // [0:2] is the sub-list for extension extendee
0, // [0:0] is the sub-list for field type_name
}
func init() { file_cmd_proto_gen_go_broker_broker_broker_proto_init() }
func file_cmd_proto_gen_go_broker_broker_broker_proto_init() {
if File_cmd_proto_gen_go_broker_broker_broker_proto != nil {
return
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_cmd_proto_gen_go_broker_broker_broker_proto_rawDesc,
NumEnums: 0,
NumMessages: 0,
NumExtensions: 2,
NumServices: 0,
},
GoTypes: file_cmd_proto_gen_go_broker_broker_broker_proto_goTypes,
DependencyIndexes: file_cmd_proto_gen_go_broker_broker_broker_proto_depIdxs,
ExtensionInfos: file_cmd_proto_gen_go_broker_broker_broker_proto_extTypes,
}.Build()
File_cmd_proto_gen_go_broker_broker_broker_proto = out.File
file_cmd_proto_gen_go_broker_broker_broker_proto_rawDesc = nil
file_cmd_proto_gen_go_broker_broker_broker_proto_goTypes = nil
file_cmd_proto_gen_go_broker_broker_broker_proto_depIdxs = nil
}

@ -0,0 +1,17 @@
syntax = "proto3";
package errors;
option go_package = "github.com/go-kratos/kratos/v2/cmd/proto-gen-go-broker;broker";
option java_multiple_files = true;
option java_package = "com.github.kratos.Broker";
option objc_class_prefix = "KratosBroker";
import "google/protobuf/descriptor.proto";
extend google.protobuf.MethodOptions {
string receive_topic = 1001011;
string output_topic = 1008611;
}

@ -0,0 +1,87 @@
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"`)
}
}

@ -0,0 +1,8 @@
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
)

@ -0,0 +1,34 @@
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")
)
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)
}
return nil
})
}

@ -1,3 +1,12 @@
package main
import (
"bytes"
"strings"
"text/template"
)
var httpTemplate = `
{{$svrType := .ServiceType}} {{$svrType := .ServiceType}}
{{$svrName := .ServiceName}} {{$svrName := .ServiceName}}
@ -7,9 +16,6 @@ const Operation{{$svrType}}{{.OriginalName}} = "/{{$svrName}}/{{.OriginalName}}"
type {{.ServiceType}}HTTPServer interface { type {{.ServiceType}}HTTPServer interface {
{{- range .MethodSets}} {{- range .MethodSets}}
{{- if ne .Comment ""}}
{{.Comment}}
{{- end}}
{{.Name}}(context.Context, *{{.Request}}) (*{{.Reply}}, error) {{.Name}}(context.Context, *{{.Request}}) (*{{.Reply}}, error)
{{- end}} {{- end}}
} }
@ -29,7 +35,7 @@ func _{{$svrType}}_{{.Name}}{{.Num}}_HTTP_Handler(srv {{$svrType}}HTTPServer) fu
if err := ctx.Bind(&in{{.Body}}); err != nil { if err := ctx.Bind(&in{{.Body}}); err != nil {
return err return err
} }
{{- if not (eq .Body "")}} {{- if not (eq .Body "")}}
if err := ctx.BindQuery(&in); err != nil { if err := ctx.BindQuery(&in); err != nil {
return err return err
@ -61,14 +67,14 @@ func _{{$svrType}}_{{.Name}}{{.Num}}_HTTP_Handler(srv {{$svrType}}HTTPServer) fu
type {{.ServiceType}}HTTPClient interface { type {{.ServiceType}}HTTPClient interface {
{{- range .MethodSets}} {{- range .MethodSets}}
{{.Name}}(ctx context.Context, req *{{.Request}}, opts ...http.CallOption) (rsp *{{.Reply}}, err error) {{.Name}}(ctx context.Context, req *{{.Request}}, opts ...http.CallOption) (rsp *{{.Reply}}, err error)
{{- end}} {{- end}}
} }
type {{.ServiceType}}HTTPClientImpl struct{ type {{.ServiceType}}HTTPClientImpl struct{
cc *http.Client cc *http.Client
} }
func New{{.ServiceType}}HTTPClient (client *http.Client) {{.ServiceType}}HTTPClient { func New{{.ServiceType}}HTTPClient (client *http.Client) {{.ServiceType}}HTTPClient {
return &{{.ServiceType}}HTTPClientImpl{client} return &{{.ServiceType}}HTTPClientImpl{client}
} }
@ -82,7 +88,7 @@ func (c *{{$svrType}}HTTPClientImpl) {{.Name}}(ctx context.Context, in *{{.Reque
opts = append(opts, http.PathTemplate(pattern)) opts = append(opts, http.PathTemplate(pattern))
{{if .HasBody -}} {{if .HasBody -}}
err := c.cc.Invoke(ctx, "{{.Method}}", path, in{{.Body}}, &out{{.ResponseBody}}, opts...) err := c.cc.Invoke(ctx, "{{.Method}}", path, in{{.Body}}, &out{{.ResponseBody}}, opts...)
{{else -}} {{else -}}
err := c.cc.Invoke(ctx, "{{.Method}}", path, nil, &out{{.ResponseBody}}, opts...) err := c.cc.Invoke(ctx, "{{.Method}}", path, nil, &out{{.ResponseBody}}, opts...)
{{end -}} {{end -}}
if err != nil { if err != nil {
@ -91,3 +97,44 @@ func (c *{{$svrType}}HTTPClientImpl) {{.Name}}(ctx context.Context, in *{{.Reque
return &out, err return &out, err
} }
{{end}} {{end}}
`
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
// 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")
}

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

@ -59,7 +59,7 @@ func generateFileContent(gen *protogen.Plugin, file *protogen.File, g *protogen.
} }
} }
func genErrorsReason(_ *protogen.Plugin, _ *protogen.File, g *protogen.GeneratedFile, enum *protogen.Enum) bool { func genErrorsReason(gen *protogen.Plugin, file *protogen.File, g *protogen.GeneratedFile, enum *protogen.Enum) bool {
defaultCode := proto.GetExtension(enum.Desc.Options(), errors.E_DefaultCode) defaultCode := proto.GetExtension(enum.Desc.Options(), errors.E_DefaultCode)
code := 0 code := 0
if ok := defaultCode.(int32); ok != 0 { if ok := defaultCode.(int32); ok != 0 {

@ -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 }}

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

@ -1,31 +1,9 @@
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= 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 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 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/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
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.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-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 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 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.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=

@ -2,12 +2,28 @@ package main
import ( import (
"bytes" "bytes"
_ "embed"
"text/template" "text/template"
) )
//go:embed errorsTemplate.tpl var errorsTemplate = `
var errorsTemplate string {{ 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 }}
`
type errorInfo struct { type errorInfo struct {
Name string Name string

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

@ -24,7 +24,7 @@ const (
var methodSets = make(map[string]int) var methodSets = make(map[string]int)
// generateFile generates a _http.pb.go file containing kratos errors definitions. // 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 { func generateFile(gen *protogen.Plugin, file *protogen.File, omitempty bool) *protogen.GeneratedFile {
if len(file.Services) == 0 || (omitempty && !hasHTTPRule(file.Services)) { if len(file.Services) == 0 || (omitempty && !hasHTTPRule(file.Services)) {
return nil return nil
} }
@ -42,12 +42,12 @@ func generateFile(gen *protogen.Plugin, file *protogen.File, omitempty bool, omi
g.P() g.P()
g.P("package ", file.GoPackageName) g.P("package ", file.GoPackageName)
g.P() g.P()
generateFileContent(gen, file, g, omitempty, omitemptyPrefix) generateFileContent(gen, file, g, omitempty)
return g return g
} }
// generateFileContent generates the kratos errors definitions, excluding the package statement. // 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) { func generateFileContent(gen *protogen.Plugin, file *protogen.File, g *protogen.GeneratedFile, omitempty bool) {
if len(file.Services) == 0 { if len(file.Services) == 0 {
return return
} }
@ -59,11 +59,11 @@ func generateFileContent(gen *protogen.Plugin, file *protogen.File, g *protogen.
g.P() g.P()
for _, service := range file.Services { for _, service := range file.Services {
genService(gen, file, g, service, omitempty, omitemptyPrefix) genService(gen, file, g, service, omitempty)
} }
} }
func genService(_ *protogen.Plugin, file *protogen.File, g *protogen.GeneratedFile, service *protogen.Service, omitempty bool, omitemptyPrefix string) { func genService(gen *protogen.Plugin, file *protogen.File, g *protogen.GeneratedFile, service *protogen.Service, omitempty bool) {
if service.Desc.Options().(*descriptorpb.ServiceOptions).GetDeprecated() { if service.Desc.Options().(*descriptorpb.ServiceOptions).GetDeprecated() {
g.P("//") g.P("//")
g.P(deprecationComment) g.P(deprecationComment)
@ -81,11 +81,11 @@ func genService(_ *protogen.Plugin, file *protogen.File, g *protogen.GeneratedFi
rule, ok := proto.GetExtension(method.Desc.Options(), annotations.E_Http).(*annotations.HttpRule) rule, ok := proto.GetExtension(method.Desc.Options(), annotations.E_Http).(*annotations.HttpRule)
if rule != nil && ok { if rule != nil && ok {
for _, bind := range rule.AdditionalBindings { for _, bind := range rule.AdditionalBindings {
sd.Methods = append(sd.Methods, buildHTTPRule(g, service, method, bind, omitemptyPrefix)) sd.Methods = append(sd.Methods, buildHTTPRule(g, method, bind))
} }
sd.Methods = append(sd.Methods, buildHTTPRule(g, service, method, rule, omitemptyPrefix)) sd.Methods = append(sd.Methods, buildHTTPRule(g, method, rule))
} else if !omitempty { } else if !omitempty {
path := fmt.Sprintf("%s/%s/%s", omitemptyPrefix, service.Desc.FullName(), method.Desc.Name()) path := fmt.Sprintf("/%s/%s", service.Desc.FullName(), method.Desc.Name())
sd.Methods = append(sd.Methods, buildMethodDesc(g, method, http.MethodPost, path)) sd.Methods = append(sd.Methods, buildMethodDesc(g, method, http.MethodPost, path))
} }
} }
@ -109,7 +109,7 @@ func hasHTTPRule(services []*protogen.Service) bool {
return false return false
} }
func buildHTTPRule(g *protogen.GeneratedFile, service *protogen.Service, m *protogen.Method, rule *annotations.HttpRule, omitemptyPrefix string) *methodDesc { func buildHTTPRule(g *protogen.GeneratedFile, m *protogen.Method, rule *annotations.HttpRule) *methodDesc {
var ( var (
path string path string
method string method string
@ -137,12 +137,6 @@ func buildHTTPRule(g *protogen.GeneratedFile, service *protogen.Service, m *prot
path = pattern.Custom.Path path = pattern.Custom.Path
method = pattern.Custom.Kind 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 body = rule.Body
responseBody = rule.ResponseBody responseBody = rule.ResponseBody
md := buildMethodDesc(g, m, method, path) md := buildMethodDesc(g, m, method, path)
@ -204,17 +198,12 @@ func buildMethodDesc(g *protogen.GeneratedFile, m *protogen.Method, method, path
} }
} }
} }
comment := m.Comments.Leading.String() + m.Comments.Trailing.String()
if comment != "" {
comment = "// " + m.GoName + strings.TrimPrefix(strings.TrimSuffix(comment, "\n"), "//")
}
return &methodDesc{ return &methodDesc{
Name: m.GoName, Name: m.GoName,
OriginalName: string(m.Desc.Name()), OriginalName: string(m.Desc.Name()),
Num: methodSets[m.GoName], Num: methodSets[m.GoName],
Request: g.QualifiedGoIdent(m.Input.GoIdent), Request: g.QualifiedGoIdent(m.Input.GoIdent),
Reply: g.QualifiedGoIdent(m.Output.GoIdent), Reply: g.QualifiedGoIdent(m.Output.GoIdent),
Comment: comment,
Path: path, Path: path,
Method: method, Method: method,
HasVars: len(vars) > 0, HasVars: len(vars) > 0,

@ -9,9 +9,8 @@ import (
) )
var ( var (
showVersion = flag.Bool("version", false, "print the version and exit") showVersion = flag.Bool("version", false, "print the version and exit")
omitempty = flag.Bool("omitempty", true, "omit if google.api is empty") omitempty = flag.Bool("omitempty", true, "omit if google.api is empty")
omitemptyPrefix = flag.String("omitempty_prefix", "", "omit if google.api is empty")
) )
func main() { func main() {
@ -28,7 +27,7 @@ func main() {
if !f.Generate { if !f.Generate {
continue continue
} }
generateFile(gen, f, *omitempty, *omitemptyPrefix) generateFile(gen, f, *omitempty)
} }
return nil return nil
}) })

@ -2,13 +2,102 @@ package main
import ( import (
"bytes" "bytes"
_ "embed"
"strings" "strings"
"text/template" "text/template"
) )
//go:embed httpTemplate.tpl var httpTemplate = `
var httpTemplate string {{$svrType := .ServiceType}}
{{$svrName := .ServiceName}}
{{- range .MethodSets}}
const Operation{{$svrType}}{{.OriginalName}} = "/{{$svrName}}/{{.OriginalName}}"
{{- end}}
type {{.ServiceType}}HTTPServer interface {
{{- range .MethodSets}}
{{.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}}
`
type serviceDesc struct { type serviceDesc struct {
ServiceType string // Greeter ServiceType string // Greeter
@ -25,7 +114,6 @@ type methodDesc struct {
Num int Num int
Request string Request string
Reply string Reply string
Comment string
// http_rule // http_rule
Path string Path string
Method string Method string

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

@ -15,13 +15,13 @@ import (
"github.com/go-kratos/kratos/v2/log" "github.com/go-kratos/kratos/v2/log"
) )
var _ Config = (*config)(nil)
var ( var (
// ErrNotFound is key not found. // ErrNotFound is key not found.
ErrNotFound = errors.New("key not found") ErrNotFound = errors.New("key not found")
// ErrTypeAssert is type assert error. // ErrTypeAssert is type assert error.
ErrTypeAssert = errors.New("type assert error") ErrTypeAssert = errors.New("type assert error")
_ Config = (*config)(nil)
) )
// Observer is config observer. // Observer is config observer.
@ -44,7 +44,7 @@ type config struct {
watchers []Watcher watchers []Watcher
} }
// New a config with options. // New new a config with options.
func New(opts ...Option) Config { func New(opts ...Option) Config {
o := options{ o := options{
decoder: defaultDecoder, decoder: defaultDecoder,
@ -62,11 +62,11 @@ func New(opts ...Option) Config {
func (c *config) watch(w Watcher) { func (c *config) watch(w Watcher) {
for { for {
kvs, err := w.Next() kvs, err := w.Next()
if errors.Is(err, context.Canceled) {
log.Infof("watcher's ctx cancel : %v", err)
return
}
if err != nil { if err != nil {
if errors.Is(err, context.Canceled) {
log.Infof("watcher's ctx cancel : %v", err)
return
}
time.Sleep(time.Second) time.Sleep(time.Second)
log.Errorf("failed to watch next config: %v", err) log.Errorf("failed to watch next config: %v", err)
continue continue

@ -2,6 +2,7 @@ package config
import ( import (
"errors" "errors"
"reflect"
"testing" "testing"
) )
@ -123,7 +124,7 @@ func TestConfig(t *testing.T) {
) )
err = c.Close() err = c.Close()
if err != nil { if err != nil {
t.Fatal(err) t.Fatal("t is not nil")
} }
jSource := newTestJSONSource(_testJSON) jSource := newTestJSONSource(_testJSON)
@ -138,21 +139,21 @@ func TestConfig(t *testing.T) {
err = cf.Load() err = cf.Load()
if err != nil { if err != nil {
t.Fatal(err) t.Fatal("t is not nil")
} }
driver, err := cf.Value("data.database.driver").String() val, err := cf.Value("data.database.driver").String()
if err != nil { if err != nil {
t.Fatal(err) t.Fatal("t is not nil")
} }
if databaseDriver != driver { if !reflect.DeepEqual(databaseDriver, val) {
t.Fatal("databaseDriver is not equal to val") t.Fatal(`databaseDriver is not equal to val`)
} }
err = cf.Watch("endpoints", func(key string, value Value) { err = cf.Watch("endpoints", func(key string, value Value) {
}) })
if err != nil { if err != nil {
t.Fatal(err) t.Fatal("t is not nil")
} }
jSource.sig <- struct{}{} jSource.sig <- struct{}{}
@ -161,24 +162,24 @@ func TestConfig(t *testing.T) {
var testConf testConfigStruct var testConf testConfigStruct
err = cf.Scan(&testConf) err = cf.Scan(&testConf)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal("t is not nil")
} }
if httpAddr != testConf.Server.HTTP.Addr { if !reflect.DeepEqual(httpAddr, testConf.Server.HTTP.Addr) {
t.Errorf("testConf.Server.HTTP.Addr want: %s, got: %s", httpAddr, testConf.Server.HTTP.Addr) t.Fatal(`httpAddr is not equal to testConf.Server.HTTP.Addr`)
} }
if httpTimeout != testConf.Server.HTTP.Timeout { if !reflect.DeepEqual(httpTimeout, testConf.Server.HTTP.Timeout) {
t.Errorf("testConf.Server.HTTP.Timeout want: %.1f, got: %.1f", httpTimeout, testConf.Server.HTTP.Timeout) t.Fatal(`httpTimeout is not equal to testConf.Server.HTTP.Timeout`)
} }
if !testConf.Server.HTTP.EnableSSL { if !reflect.DeepEqual(true, testConf.Server.HTTP.EnableSSL) {
t.Error("testConf.Server.HTTP.EnableSSL is not equal to true") t.Fatal(`testConf.Server.HTTP.EnableSSL is not equal to true`)
} }
if grpcPort != testConf.Server.GRPC.Port { if !reflect.DeepEqual(grpcPort, testConf.Server.GRPC.Port) {
t.Errorf("testConf.Server.GRPC.Port want: %d, got: %d", grpcPort, testConf.Server.GRPC.Port) t.Fatal(`grpcPort is not equal to testConf.Server.GRPC.Port`)
} }
if endpoint1 != testConf.Endpoints[0] { if !reflect.DeepEqual(endpoint1, testConf.Endpoints[0]) {
t.Errorf("testConf.Endpoints[0] want: %s, got: %s", endpoint1, testConf.Endpoints[0]) t.Fatal(`endpoint1 is not equal to testConf.Endpoints[0]`)
} }
if len(testConf.Endpoints) != 2 { if !reflect.DeepEqual(len(testConf.Endpoints), 2) {
t.Error("len(testConf.Endpoints) is not equal to 2") t.Fatal(`len(testConf.Endpoints) is not equal to 2`)
} }
} }

6
config/env/env.go vendored

@ -19,11 +19,11 @@ func (e *env) Load() (kv []*config.KeyValue, err error) {
return e.load(os.Environ()), nil return e.load(os.Environ()), nil
} }
func (e *env) load(envs []string) []*config.KeyValue { func (e *env) load(envStrings []string) []*config.KeyValue {
var kv []*config.KeyValue var kv []*config.KeyValue
for _, env := range envs { for _, envstr := range envStrings {
var k, v string var k, v string
subs := strings.SplitN(env, "=", 2) //nolint:gomnd subs := strings.SplitN(envstr, "=", 2) //nolint:gomnd
k = subs[0] k = subs[0]
if len(subs) > 1 { if len(subs) > 1 {
v = subs[1] v = subs[1]

@ -6,13 +6,13 @@ import (
"github.com/go-kratos/kratos/v2/config" "github.com/go-kratos/kratos/v2/config"
) )
var _ config.Watcher = (*watcher)(nil)
type watcher struct { type watcher struct {
ctx context.Context ctx context.Context
cancel context.CancelFunc cancel context.CancelFunc
} }
var _ config.Watcher = (*watcher)(nil)
func NewWatcher() (config.Watcher, error) { func NewWatcher() (config.Watcher, error) {
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
return &watcher{ctx: ctx, cancel: cancel}, nil return &watcher{ctx: ctx, cancel: cancel}, nil

@ -121,10 +121,10 @@ func testWatchFile(t *testing.T, path string) {
} }
kvs, err := watch.Next() kvs, err := watch.Next()
if err != nil { if err != nil {
t.Errorf("watch.Next() error(%v)", err) t.Errorf(`watch.Next() error(%v)`, err)
} }
if !reflect.DeepEqual(string(kvs[0].Value), _testJSONUpdate) { 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) 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") newFilepath := filepath.Join(filepath.Dir(path), "test1.json")
@ -133,15 +133,15 @@ func testWatchFile(t *testing.T, path string) {
} }
kvs, err = watch.Next() kvs, err = watch.Next()
if err == nil { if err == nil {
t.Errorf("watch.Next() error(%v)", err) t.Errorf(`watch.Next() error(%v)`, err)
} }
if kvs != nil { if kvs != nil {
t.Errorf("watch.Next() error(%v)", err) t.Errorf(`watch.Next() error(%v)`, err)
} }
err = watch.Stop() err = watch.Stop()
if err != nil { if err != nil {
t.Errorf("watch.Stop() error(%v)", err) t.Errorf(`watch.Stop() error(%v)`, err)
} }
if err := os.Rename(newFilepath, path); err != nil { if err := os.Rename(newFilepath, path); err != nil {
@ -171,10 +171,10 @@ func testWatchDir(t *testing.T, path, file string) {
kvs, err := watch.Next() kvs, err := watch.Next()
if err != nil { if err != nil {
t.Errorf("watch.Next() error(%v)", err) t.Errorf(`watch.Next() error(%v)`, err)
} }
if !reflect.DeepEqual(string(kvs[0].Value), _testJSONUpdate) { 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) t.Errorf(`string(kvs[0].Value(%v) is not equal to _testJSONUpdate(%v)`, kvs[0].Value, _testJSONUpdate)
} }
} }

@ -1,8 +1,6 @@
package file package file
import ( import "testing"
"testing"
)
func TestFormat(t *testing.T) { func TestFormat(t *testing.T) {
tests := []struct { tests := []struct {

@ -10,8 +10,6 @@ import (
"github.com/go-kratos/kratos/v2/config" "github.com/go-kratos/kratos/v2/config"
) )
var _ config.Watcher = (*watcher)(nil)
type watcher struct { type watcher struct {
f *file f *file
fw *fsnotify.Watcher fw *fsnotify.Watcher
@ -20,6 +18,8 @@ type watcher struct {
cancel context.CancelFunc cancel context.CancelFunc
} }
var _ config.Watcher = (*watcher)(nil)
func newWatcher(f *file) (config.Watcher, error) { func newWatcher(f *file) (config.Watcher, error) {
fw, err := fsnotify.NewWatcher() fw, err := fsnotify.NewWatcher()
if err != nil { if err != nil {

@ -51,7 +51,7 @@ func WithResolver(r Resolver) Option {
// WithLogger with config logger. // WithLogger with config logger.
// Deprecated: use global logger instead. // Deprecated: use global logger instead.
func WithLogger(_ log.Logger) Option { func WithLogger(l log.Logger) Option {
return func(o *options) {} return func(o *options) {}
} }

@ -15,7 +15,7 @@ func TestDefaultDecoder(t *testing.T) {
target := make(map[string]interface{}) target := make(map[string]interface{})
err := defaultDecoder(src, target) err := defaultDecoder(src, target)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal("err is not nil")
} }
if !reflect.DeepEqual(target, map[string]interface{}{"service": []byte("config")}) { if !reflect.DeepEqual(target, map[string]interface{}{"service": []byte("config")}) {
t.Fatal(`target is not equal to map[string]interface{}{"service": "config"}`) t.Fatal(`target is not equal to map[string]interface{}{"service": "config"}`)
@ -29,7 +29,7 @@ func TestDefaultDecoder(t *testing.T) {
target = make(map[string]interface{}) target = make(map[string]interface{})
err = defaultDecoder(src, target) err = defaultDecoder(src, target)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal("err is not nil")
} }
if !reflect.DeepEqual(map[string]interface{}{ if !reflect.DeepEqual(map[string]interface{}{
"service": map[string]interface{}{ "service": map[string]interface{}{
@ -150,7 +150,7 @@ func TestDefaultResolver(t *testing.T) {
t.Run(test.name, func(t *testing.T) { t.Run(test.name, func(t *testing.T) {
err := defaultResolver(data) err := defaultResolver(data)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(`err is not nil`)
} }
rd := reader{ rd := reader{
values: data, values: data,
@ -161,25 +161,25 @@ func TestDefaultResolver(t *testing.T) {
case int: case int:
if actual, err = v.Int(); err == nil { if actual, err = v.Int(); err == nil {
if !reflect.DeepEqual(test.expect.(int), int(actual.(int64))) { if !reflect.DeepEqual(test.expect.(int), int(actual.(int64))) {
t.Fatal("expect is not equal to actual") t.Fatal(`expect is not equal to actual`)
} }
} }
case string: case string:
if actual, err = v.String(); err == nil { if actual, err = v.String(); err == nil {
if !reflect.DeepEqual(test.expect, actual) { if !reflect.DeepEqual(test.expect, actual) {
t.Fatal("expect is not equal to actual") t.Fatal(`expect is not equal to actual`)
} }
} }
case bool: case bool:
if actual, err = v.Bool(); err == nil { if actual, err = v.Bool(); err == nil {
if !reflect.DeepEqual(test.expect, actual) { if !reflect.DeepEqual(test.expect, actual) {
t.Fatal("expect is not equal to actual") t.Fatal(`expect is not equal to actual`)
} }
} }
case float64: case float64:
if actual, err = v.Float(); err == nil { if actual, err = v.Float(); err == nil {
if !reflect.DeepEqual(test.expect, actual) { if !reflect.DeepEqual(test.expect, actual) {
t.Fatal("expect is not equal to actual") t.Fatal(`expect is not equal to actual`)
} }
} }
default: default:

@ -8,11 +8,11 @@ import (
"strings" "strings"
"sync" "sync"
"github.com/go-kratos/kratos/v2/log"
"github.com/imdario/mergo" "github.com/imdario/mergo"
"google.golang.org/protobuf/encoding/protojson" "google.golang.org/protobuf/encoding/protojson"
"google.golang.org/protobuf/proto" "google.golang.org/protobuf/proto"
"github.com/go-kratos/kratos/v2/log"
) )
// Reader is config reader. // Reader is config reader.
@ -38,7 +38,9 @@ func newReader(opts options) Reader {
} }
func (r *reader) Merge(kvs ...*KeyValue) error { func (r *reader) Merge(kvs ...*KeyValue) error {
merged, err := r.cloneMap() r.lock.Lock()
merged, err := cloneMap(r.values)
r.lock.Unlock()
if err != nil { if err != nil {
return err return err
} }
@ -77,12 +79,6 @@ func (r *reader) Resolve() error {
return r.opts.resolver(r.values) return r.opts.resolver(r.values)
} }
func (r *reader) cloneMap() (map[string]interface{}, error) {
r.lock.Lock()
defer r.lock.Unlock()
return cloneMap(r.values)
}
func cloneMap(src map[string]interface{}) (map[string]interface{}, error) { func cloneMap(src map[string]interface{}) (map[string]interface{}, error) {
// https://gist.github.com/soroushjp/0ec92102641ddfc3ad5515ca76405f4d // https://gist.github.com/soroushjp/0ec92102641ddfc3ad5515ca76405f4d
var buf bytes.Buffer var buf bytes.Buffer
@ -94,12 +90,12 @@ func cloneMap(src map[string]interface{}) (map[string]interface{}, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
var clone map[string]interface{} var copy map[string]interface{}
err = dec.Decode(&clone) err = dec.Decode(&copy)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return clone, nil return copy, nil
} }
func convertMap(src interface{}) interface{} { func convertMap(src interface{}) interface{} {

@ -29,7 +29,7 @@ func TestReader_Merge(t *testing.T) {
Format: "json", Format: "json",
}) })
if err == nil { if err == nil {
t.Fatal("err is nil") t.Fatal(`err is nil`)
} }
err = r.Merge(&KeyValue{ err = r.Merge(&KeyValue{
@ -38,15 +38,15 @@ func TestReader_Merge(t *testing.T) {
Format: "json", Format: "json",
}) })
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(`err is not nil`)
} }
vv, ok := r.Value("nice") vv, ok := r.Value("nice")
if !ok { if !ok {
t.Fatal("ok is false") t.Fatal(`ok is false`)
} }
vvv, err := vv.String() vvv, err := vv.String()
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(`err is not nil`)
} }
if vvv != "boat" { if vvv != "boat" {
t.Fatal(`vvv is not equal to "boat"`) t.Fatal(`vvv is not equal to "boat"`)
@ -58,18 +58,18 @@ func TestReader_Merge(t *testing.T) {
Format: "json", Format: "json",
}) })
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(`err is not nil`)
} }
vv, ok = r.Value("x") vv, ok = r.Value("x")
if !ok { if !ok {
t.Fatal("ok is false") t.Fatal(`ok is false`)
} }
vvx, err := vv.Int() vvx, err := vv.Int()
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(`err is not nil`)
} }
if vvx != 2 { if int64(2) != vvx {
t.Fatal("vvx is not equal to 2") t.Fatal(`vvx is not equal to 2`)
} }
} }
@ -118,27 +118,27 @@ a:
r := newReader(opts) r := newReader(opts)
err := r.Merge(&test.kv) err := r.Merge(&test.kv)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(`err is not nil`)
} }
vv, ok := r.Value("a.b.X") vv, ok := r.Value("a.b.X")
if !ok { if !ok {
t.Fatal("ok is false") t.Fatal(`ok is false`)
} }
vvv, err := vv.Int() vvv, err := vv.Int()
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(`err is not nil`)
} }
if int64(1) != vvv { if int64(1) != vvv {
t.Fatal("vvv is not equal to 1") t.Fatal(`vvv is not equal to 1`)
} }
vv, ok = r.Value("a.b.Y") vv, ok = r.Value("a.b.Y")
if !ok { if !ok {
t.Fatal("ok is false") t.Fatal(`ok is false`)
} }
vvy, err := vv.String() vvy, err := vv.String()
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(`err is not nil`)
} }
if vvy != "lol" { if vvy != "lol" {
t.Fatal(`vvy is not equal to "lol"`) t.Fatal(`vvy is not equal to "lol"`)
@ -146,29 +146,29 @@ a:
vv, ok = r.Value("a.b.z") vv, ok = r.Value("a.b.z")
if !ok { if !ok {
t.Fatal("ok is false") t.Fatal(`ok is false`)
} }
vvz, err := vv.Bool() vvz, err := vv.Bool()
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(`err is not nil`)
} }
if !vvz { if !vvz {
t.Fatal("vvz is not equal to true") t.Fatal(`vvz is not equal to true`)
} }
_, ok = r.Value("aasasdg=234l.asdfk,") _, ok = r.Value("aasasdg=234l.asdfk,")
if ok { if ok {
t.Fatal("ok is true") t.Fatal(`ok is true`)
} }
_, ok = r.Value("aas......asdg=234l.asdfk,") _, ok = r.Value("aas......asdg=234l.asdfk,")
if ok { if ok {
t.Fatal("ok is true") t.Fatal(`ok is true`)
} }
_, ok = r.Value("a.b.Y.") _, ok = r.Value("a.b.Y.")
if ok { if ok {
t.Fatal("ok is true") t.Fatal(`ok is true`)
} }
}) })
} }
@ -192,11 +192,11 @@ func TestReader_Source(t *testing.T) {
Format: "json", Format: "json",
}) })
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(`err is not nil`)
} }
b, err := r.Source() b, err := r.Source()
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(`err is not nil`)
} }
if !reflect.DeepEqual([]byte(`{"a":{"b":{"X":1}}}`), b) { if !reflect.DeepEqual([]byte(`{"a":{"b":{"X":1}}}`), b) {
t.Fatal("[]byte(`{\"a\":{\"b\":{\"X\":1}}}`) is not equal to b") t.Fatal("[]byte(`{\"a\":{\"b\":{\"X\":1}}}`) is not equal to b")

@ -1,7 +1,7 @@
package config package config
import ( import (
"encoding/json" stdjson "encoding/json"
"fmt" "fmt"
"reflect" "reflect"
"strconv" "strconv"
@ -10,7 +10,7 @@ import (
"google.golang.org/protobuf/proto" "google.golang.org/protobuf/proto"
kratosjson "github.com/go-kratos/kratos/v2/encoding/json" "github.com/go-kratos/kratos/v2/encoding/json"
) )
var ( var (
@ -36,10 +36,6 @@ type atomicValue struct {
atomic.Value atomic.Value
} }
func (v *atomicValue) typeAssertError() error {
return fmt.Errorf("type assert to %v failed", reflect.TypeOf(v.Load()))
}
func (v *atomicValue) Bool() (bool, error) { func (v *atomicValue) Bool() (bool, error) {
switch val := v.Load().(type) { switch val := v.Load().(type) {
case bool: case bool:
@ -47,7 +43,7 @@ func (v *atomicValue) Bool() (bool, error) {
case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, float32, float64, string: case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, float32, float64, string:
return strconv.ParseBool(fmt.Sprint(val)) return strconv.ParseBool(fmt.Sprint(val))
} }
return false, v.typeAssertError() return false, fmt.Errorf("type assert to %v failed", reflect.TypeOf(v.Load()))
} }
func (v *atomicValue) Int() (int64, error) { func (v *atomicValue) Int() (int64, error) {
@ -77,37 +73,35 @@ func (v *atomicValue) Int() (int64, error) {
case float64: case float64:
return int64(val), nil return int64(val), nil
case string: case string:
return strconv.ParseInt(val, 10, 64) return strconv.ParseInt(val, 10, 64) //nolint:gomnd
} }
return 0, v.typeAssertError() return 0, fmt.Errorf("type assert to %v failed", reflect.TypeOf(v.Load()))
} }
func (v *atomicValue) Slice() ([]Value, error) { func (v *atomicValue) Slice() ([]Value, error) {
vals, ok := v.Load().([]interface{}) if vals, ok := v.Load().([]interface{}); ok {
if !ok { var slices []Value
return nil, v.typeAssertError() for _, val := range vals {
a := &atomicValue{}
a.Store(val)
slices = append(slices, a)
}
return slices, nil
} }
slices := make([]Value, 0, len(vals)) return nil, fmt.Errorf("type assert to %v failed", reflect.TypeOf(v.Load()))
for _, val := range vals {
a := new(atomicValue)
a.Store(val)
slices = append(slices, a)
}
return slices, nil
} }
func (v *atomicValue) Map() (map[string]Value, error) { func (v *atomicValue) Map() (map[string]Value, error) {
vals, ok := v.Load().(map[string]interface{}) if vals, ok := v.Load().(map[string]interface{}); ok {
if !ok { m := make(map[string]Value, len(vals))
return nil, v.typeAssertError() for key, val := range vals {
} a := new(atomicValue)
m := make(map[string]Value, len(vals)) a.Store(val)
for key, val := range vals { m[key] = a
a := new(atomicValue) }
a.Store(val) return m, nil
m[key] = a
} }
return m, nil return nil, fmt.Errorf("type assert to %v failed", reflect.TypeOf(v.Load()))
} }
func (v *atomicValue) Float() (float64, error) { func (v *atomicValue) Float() (float64, error) {
@ -137,9 +131,9 @@ func (v *atomicValue) Float() (float64, error) {
case float64: case float64:
return val, nil return val, nil
case string: case string:
return strconv.ParseFloat(val, 64) return strconv.ParseFloat(val, 64) //nolint:gomnd
} }
return 0.0, v.typeAssertError() return 0.0, fmt.Errorf("type assert to %v failed", reflect.TypeOf(v.Load()))
} }
func (v *atomicValue) String() (string, error) { func (v *atomicValue) String() (string, error) {
@ -153,7 +147,7 @@ func (v *atomicValue) String() (string, error) {
case fmt.Stringer: case fmt.Stringer:
return val.String(), nil return val.String(), nil
} }
return "", v.typeAssertError() return "", fmt.Errorf("type assert to %v failed", reflect.TypeOf(v.Load()))
} }
func (v *atomicValue) Duration() (time.Duration, error) { func (v *atomicValue) Duration() (time.Duration, error) {
@ -165,14 +159,14 @@ func (v *atomicValue) Duration() (time.Duration, error) {
} }
func (v *atomicValue) Scan(obj interface{}) error { func (v *atomicValue) Scan(obj interface{}) error {
data, err := json.Marshal(v.Load()) data, err := stdjson.Marshal(v.Load())
if err != nil { if err != nil {
return err return err
} }
if pb, ok := obj.(proto.Message); ok { if pb, ok := obj.(proto.Message); ok {
return kratosjson.UnmarshalOptions.Unmarshal(data, pb) return json.UnmarshalOptions.Unmarshal(data, pb)
} }
return json.Unmarshal(data, obj) return stdjson.Unmarshal(data, obj)
} }
type errValue struct { type errValue struct {

@ -6,17 +6,17 @@ import (
"time" "time"
) )
func TestAtomicValue_Bool(t *testing.T) { func Test_atomicValue_Bool(t *testing.T) {
vlist := []interface{}{"1", "t", "T", "true", "TRUE", "True", true, 1, int32(1)} vlist := []interface{}{"1", "t", "T", "true", "TRUE", "True", true, 1, int32(1)}
for _, x := range vlist { for _, x := range vlist {
v := atomicValue{} v := atomicValue{}
v.Store(x) v.Store(x)
b, err := v.Bool() b, err := v.Bool()
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(`err is not nil`)
} }
if !b { if !b {
t.Fatal("b is not equal to true") t.Fatal(`b is not equal to true`)
} }
} }
@ -26,10 +26,10 @@ func TestAtomicValue_Bool(t *testing.T) {
v.Store(x) v.Store(x)
b, err := v.Bool() b, err := v.Bool()
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(`err is not nil`)
} }
if b { if b {
t.Fatal("b is not equal to false") t.Fatal(`b is not equal to false`)
} }
} }
@ -39,22 +39,22 @@ func TestAtomicValue_Bool(t *testing.T) {
v.Store(x) v.Store(x)
_, err := v.Bool() _, err := v.Bool()
if err == nil { if err == nil {
t.Fatal("err is nil") t.Fatal(`err is nil`)
} }
} }
} }
func TestAtomicValue_Int(t *testing.T) { func Test_atomicValue_Int(t *testing.T) {
vlist := []interface{}{"123123", float64(123123), int64(123123), int32(123123), 123123} vlist := []interface{}{"123123", float64(123123), int64(123123), int32(123123), 123123}
for _, x := range vlist { for _, x := range vlist {
v := atomicValue{} v := atomicValue{}
v.Store(x) v.Store(x)
b, err := v.Int() b, err := v.Int()
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(`err is not nil`)
} }
if b != 123123 { if b != 123123 {
t.Fatal("b is not equal to 123123") t.Fatal(`b is not equal to 123123`)
} }
} }
@ -64,22 +64,22 @@ func TestAtomicValue_Int(t *testing.T) {
v.Store(x) v.Store(x)
_, err := v.Int() _, err := v.Int()
if err == nil { if err == nil {
t.Fatal("err is nil") t.Fatal(`err is nil`)
} }
} }
} }
func TestAtomicValue_Float(t *testing.T) { func Test_atomicValue_Float(t *testing.T) {
vlist := []interface{}{"123123.1", 123123.1} vlist := []interface{}{"123123.1", 123123.1}
for _, x := range vlist { for _, x := range vlist {
v := atomicValue{} v := atomicValue{}
v.Store(x) v.Store(x)
b, err := v.Float() b, err := v.Float()
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(`err is not nil`)
} }
if b != 123123.1 { if b != 123123.1 {
t.Fatal("b is not equal to 123123.1") t.Fatal(`b is not equal to 123123.1`)
} }
} }
@ -89,7 +89,7 @@ func TestAtomicValue_Float(t *testing.T) {
v.Store(x) v.Store(x)
_, err := v.Float() _, err := v.Float()
if err == nil { if err == nil {
t.Fatal("err is nil") t.Fatal(`err is nil`)
} }
} }
} }
@ -103,17 +103,17 @@ func (t ts) String() string {
return fmt.Sprintf("%s%d", t.Name, t.Age) return fmt.Sprintf("%s%d", t.Name, t.Age)
} }
func TestAtomicValue_String(t *testing.T) { func Test_atomicValue_String(t *testing.T) {
vlist := []interface{}{"1", float64(1), int64(1), 1, int64(1)} vlist := []interface{}{"1", float64(1), int64(1), 1, int64(1)}
for _, x := range vlist { for _, x := range vlist {
v := atomicValue{} v := atomicValue{}
v.Store(x) v.Store(x)
b, err := v.String() b, err := v.String()
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(`err is not nil`)
} }
if b != "1" { if b != "1" {
t.Fatal("b is not equal to 1") t.Fatal(`b is not equal to 1`)
} }
} }
@ -121,7 +121,7 @@ func TestAtomicValue_String(t *testing.T) {
v.Store(true) v.Store(true)
b, err := v.String() b, err := v.String()
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(`err is not nil`)
} }
if b != "true" { if b != "true" {
t.Fatal(`b is not equal to "true"`) t.Fatal(`b is not equal to "true"`)
@ -134,48 +134,48 @@ func TestAtomicValue_String(t *testing.T) {
}) })
b, err = v.String() b, err = v.String()
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(`err is not nil`)
} }
if b != "test10" { if b != "test10" {
t.Fatal(`b is not equal to "test10"`) t.Fatal(`b is not equal to "test10"`)
} }
} }
func TestAtomicValue_Duration(t *testing.T) { func Test_atomicValue_Duration(t *testing.T) {
vlist := []interface{}{int64(5)} vlist := []interface{}{int64(5)}
for _, x := range vlist { for _, x := range vlist {
v := atomicValue{} v := atomicValue{}
v.Store(x) v.Store(x)
b, err := v.Duration() b, err := v.Duration()
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(`err is not nil`)
} }
if b != time.Duration(5) { if b != time.Duration(5) {
t.Fatal("b is not equal to time.Duration(5)") t.Fatal(`b is not equal to time.Duration(5)`)
} }
} }
} }
func TestAtomicValue_Slice(t *testing.T) { func Test_atomicValue_Slice(t *testing.T) {
vlist := []interface{}{int64(5)} vlist := []interface{}{int64(5)}
v := atomicValue{} v := atomicValue{}
v.Store(vlist) v.Store(vlist)
slices, err := v.Slice() slices, err := v.Slice()
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(`err is not nil`)
} }
for _, v := range slices { for _, v := range slices {
b, err := v.Duration() b, err := v.Duration()
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(`err is not nil`)
} }
if b != time.Duration(5) { if b != time.Duration(5) {
t.Fatal("b is not equal to time.Duration(5)") t.Fatal(`b is not equal to time.Duration(5)`)
} }
} }
} }
func TestAtomicValue_Map(t *testing.T) { func Test_atomicValue_Map(t *testing.T) {
vlist := make(map[string]interface{}) vlist := make(map[string]interface{})
vlist["5"] = int64(5) vlist["5"] = int64(5)
vlist["text"] = "text" vlist["text"] = "text"
@ -183,21 +183,21 @@ func TestAtomicValue_Map(t *testing.T) {
v.Store(vlist) v.Store(vlist)
m, err := v.Map() m, err := v.Map()
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(`err is not nil`)
} }
for k, v := range m { for k, v := range m {
if k == "5" { if k == "5" {
b, err := v.Duration() b, err := v.Duration()
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(`err is not nil`)
} }
if b != time.Duration(5) { if b != time.Duration(5) {
t.Fatal("b is not equal to time.Duration(5)") t.Fatal(`b is not equal to time.Duration(5)`)
} }
} else { } else {
b, err := v.String() b, err := v.String()
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(`err is not nil`)
} }
if b != "text" { if b != "text" {
t.Fatal(`b is not equal to "text"`) t.Fatal(`b is not equal to "text"`)
@ -206,19 +206,20 @@ func TestAtomicValue_Map(t *testing.T) {
} }
} }
func TestAtomicValue_Scan(t *testing.T) { func Test_atomicValue_Scan(t *testing.T) {
var err error
v := atomicValue{} v := atomicValue{}
err := v.Scan(&struct { err = v.Scan(&struct {
A string `json:"a"` A string `json:"a"`
}{"a"}) }{"a"})
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(`err is not nil`)
} }
err = v.Scan(&struct { err = v.Scan(&struct {
A string `json:"a"` A string `json:"a"`
}{"a"}) }{"a"})
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(`err is not nil`)
} }
} }

@ -97,4 +97,5 @@ config := map[string]interface{}{
} }
} }
} }
_ = config
``` ```

@ -3,14 +3,15 @@ package apollo
import ( import (
"strings" "strings"
"github.com/go-kratos/kratos/v2/config"
"github.com/go-kratos/kratos/v2/log"
"github.com/apolloconfig/agollo/v4" "github.com/apolloconfig/agollo/v4"
"github.com/apolloconfig/agollo/v4/constant" "github.com/apolloconfig/agollo/v4/constant"
apolloconfig "github.com/apolloconfig/agollo/v4/env/config" apolloConfig "github.com/apolloconfig/agollo/v4/env/config"
"github.com/apolloconfig/agollo/v4/extension" "github.com/apolloconfig/agollo/v4/extension"
"github.com/go-kratos/kratos/v2/config"
"github.com/go-kratos/kratos/v2/encoding" "github.com/go-kratos/kratos/v2/encoding"
"github.com/go-kratos/kratos/v2/log"
) )
type apollo struct { type apollo struct {
@ -25,8 +26,6 @@ const (
properties = "properties" properties = "properties"
) )
var formats map[string]struct{}
// Option is apollo option // Option is apollo option
type Option func(*options) type Option func(*options)
@ -112,8 +111,8 @@ func NewSource(opts ...Option) config.Source {
for _, o := range opts { for _, o := range opts {
o(&op) o(&op)
} }
client, err := agollo.StartWithConfig(func() (*apolloconfig.AppConfig, error) { client, err := agollo.StartWithConfig(func() (*apolloConfig.AppConfig, error) {
return &apolloconfig.AppConfig{ return &apolloConfig.AppConfig{
AppID: op.appid, AppID: op.appid,
Cluster: op.cluster, Cluster: op.cluster,
NamespaceName: op.namespace, NamespaceName: op.namespace,
@ -131,16 +130,10 @@ func NewSource(opts ...Option) config.Source {
func format(ns string) string { func format(ns string) string {
arr := strings.Split(ns, ".") arr := strings.Split(ns, ".")
suffix := arr[len(arr)-1] if len(arr) <= 1 || arr[len(arr)-1] == properties {
if len(arr) <= 1 || suffix == properties {
return json return json
} }
if _, ok := formats[suffix]; !ok { return arr[len(arr)-1]
// fallback
return json
}
return suffix
} }
func (e *apollo) load() []*config.KeyValue { func (e *apollo) load() []*config.KeyValue {
@ -157,7 +150,7 @@ func (e *apollo) load() []*config.KeyValue {
kvs = append(kvs, kv) kvs = append(kvs, kv)
continue continue
} }
if strings.Contains(ns, ".") && !strings.HasSuffix(ns, "."+properties) && if strings.Contains(ns, ".") && !strings.Contains(ns, properties) &&
(format(ns) == yaml || format(ns) == yml || format(ns) == json) { (format(ns) == yaml || format(ns) == yml || format(ns) == json) {
kv, err := e.getOriginConfig(ns) kv, err := e.getOriginConfig(ns)
if err != nil { if err != nil {
@ -166,13 +159,14 @@ func (e *apollo) load() []*config.KeyValue {
} }
kvs = append(kvs, kv) kvs = append(kvs, kv)
continue continue
} else {
kv, err := e.getConfig(ns)
if err != nil {
log.Errorf("apollo get config failed,err:%v", err)
continue
}
kvs = append(kvs, kv)
} }
kv, err := e.getConfig(ns)
if err != nil {
log.Errorf("apollo get config failed,err:%v", err)
continue
}
kvs = append(kvs, kv)
} }
return kvs return kvs
} }
@ -259,28 +253,16 @@ func resolve(key string, value interface{}, target map[string]interface{}) {
// eg: namespace.ext with subKey got namespace.subKey // eg: namespace.ext with subKey got namespace.subKey
func genKey(ns, sub string) string { func genKey(ns, sub string) string {
arr := strings.Split(ns, ".") arr := strings.Split(ns, ".")
if len(arr) < 1 {
return sub
}
if len(arr) == 1 { if len(arr) == 1 {
if ns == "" { if ns == "" {
return sub return sub
} }
return ns + "." + sub return ns + "." + sub
} }
suffix := arr[len(arr)-1] return strings.Join(arr[:len(arr)-1], ".") + "." + sub
_, ok := formats[suffix]
if ok {
return strings.Join(arr[:len(arr)-1], ".") + "." + sub
}
return ns + "." + sub
}
func init() {
formats = make(map[string]struct{})
formats[yaml] = struct{}{}
formats[yml] = struct{}{}
formats[json] = struct{}{}
formats[properties] = struct{}{}
} }

@ -1,90 +0,0 @@
package apollo
import (
"testing"
)
func Test_genKey(t *testing.T) {
type args struct {
ns string
sub string
}
tests := []struct {
name string
args args
want string
}{
{
name: "blank namespace",
args: args{
ns: "",
sub: "x.y",
},
want: "x.y",
},
{
name: "properties namespace",
args: args{
ns: "application",
sub: "x.y",
},
want: "application.x.y",
},
{
name: "namespace with format",
args: args{
ns: "app.yaml",
sub: "x.y",
},
want: "app.x.y",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := genKey(tt.args.ns, tt.args.sub); got != tt.want {
t.Errorf("genKey() = %v, want %v", got, tt.want)
}
})
}
}
func Test_format(t *testing.T) {
tests := []struct {
name string
namespace string
want string
}{
{
name: "properties namespace",
namespace: "application",
want: "json",
},
{
name: "properties namespace #1",
namespace: "app.setting",
want: "json",
},
{
name: "namespace with format[yaml]",
namespace: "app.yaml",
want: "yaml",
},
{
name: "namespace with format[yml]",
namespace: "app.yml",
want: "yml",
},
{
name: "namespace with format[json]",
namespace: "app.json",
want: "json",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := format(tt.namespace); got != tt.want {
t.Errorf("format() = %v, want %v", got, tt.want)
}
})
}
}

@ -3,10 +3,13 @@ module github.com/go-kratos/kratos/contrib/config/apollo/v2
go 1.16 go 1.16
require ( require (
github.com/apolloconfig/agollo/v4 v4.3.0 github.com/apolloconfig/agollo/v4 v4.2.0
github.com/go-kratos/kratos/v2 v2.6.3 github.com/go-kratos/kratos/v2 v2.4.0
) )
require github.com/spf13/viper v1.11.0 // indirect require (
github.com/spf13/viper v1.11.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
replace github.com/go-kratos/kratos/v2 => ../../../ replace github.com/go-kratos/kratos/v2 => ../../../

File diff suppressed because it is too large Load Diff

@ -4,11 +4,12 @@ import (
"context" "context"
"strings" "strings"
"github.com/apolloconfig/agollo/v4/storage" "github.com/go-kratos/kratos/v2/encoding"
"github.com/go-kratos/kratos/v2/config" "github.com/go-kratos/kratos/v2/config"
"github.com/go-kratos/kratos/v2/encoding"
"github.com/go-kratos/kratos/v2/log" "github.com/go-kratos/kratos/v2/log"
"github.com/apolloconfig/agollo/v4/storage"
) )
type watcher struct { type watcher struct {
@ -25,7 +26,7 @@ type customChangeListener struct {
func (c *customChangeListener) onChange(namespace string, changes map[string]*storage.ConfigChange) []*config.KeyValue { func (c *customChangeListener) onChange(namespace string, changes map[string]*storage.ConfigChange) []*config.KeyValue {
kv := make([]*config.KeyValue, 0, 2) kv := make([]*config.KeyValue, 0, 2)
if strings.Contains(namespace, ".") && !strings.HasSuffix(namespace, "."+properties) && if strings.Contains(namespace, ".") && !strings.Contains(namespace, properties) &&
(format(namespace) == yaml || format(namespace) == yml || format(namespace) == json) { (format(namespace) == yaml || format(namespace) == yml || format(namespace) == json) {
value, err := c.apollo.client.GetConfigCache(namespace).Get("content") value, err := c.apollo.client.GetConfigCache(namespace).Get("content")
if err != nil { if err != nil {
@ -72,7 +73,7 @@ func (c *customChangeListener) OnChange(changeEvent *storage.ChangeEvent) {
c.in <- change c.in <- change
} }
func (c *customChangeListener) OnNewestChange(_ *storage.FullChangeEvent) {} func (c *customChangeListener) OnNewestChange(changeEvent *storage.FullChangeEvent) {}
func newWatcher(a *apollo) (config.Watcher, error) { func newWatcher(a *apollo) (config.Watcher, error) {
changeCh := make(chan []*config.KeyValue) changeCh := make(chan []*config.KeyValue)

@ -2,21 +2,20 @@
```go ```go
import ( import (
"github.com/hashicorp/consul/api"
"github.com/go-kratos/kratos/contrib/config/consul/v2" "github.com/go-kratos/kratos/contrib/config/consul/v2"
"github.com/hashicorp/consul/api"
) )
func main() { func main() {
consulClient, err := api.NewClient(&api.Config{ consulClient, err := api.NewClient(&api.Config{
Address: "127.0.0.1:8500", Address: "127.0.0.1:8500",
}) })
if err != nil { if err != nil {
panic(err) panic(err)
} }
cs, err := consul.New(consulClient, consul.WithPath("app/cart/configs/")) cs, err := consul.New(consulClient, consul.WithPath("app/cart/configs/"))
// consul中需要标注文件后缀,kratos读取配置需要适配文件后缀 //consul中需要标注文件后缀,kratos读取配置需要适配文件后缀
// The file suffix needs to be marked, and kratos needs to adapt the file suffix to read the configuration. //The file suffix needs to be marked, and kratos needs to adapt the file suffix to read the configuration.
if err != nil { if err != nil {
panic(err) panic(err)
} }

@ -3,13 +3,8 @@ module github.com/go-kratos/kratos/contrib/config/consul/v2
go 1.15 go 1.15
require ( require (
github.com/armon/go-metrics v0.3.10 // indirect github.com/go-kratos/kratos/v2 v2.4.0
github.com/go-kratos/kratos/v2 v2.6.3 github.com/hashicorp/consul/api v1.14.0
github.com/hashicorp/consul/api v1.20.0
github.com/hashicorp/go-hclog v0.14.1 // indirect
github.com/hashicorp/go-immutable-radix v1.3.0 // indirect
github.com/hashicorp/go-msgpack v0.5.5 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
) )
replace github.com/go-kratos/kratos/v2 => ../../../ replace github.com/go-kratos/kratos/v2 => ../../../

File diff suppressed because it is too large Load Diff

@ -18,7 +18,7 @@ type watcher struct {
cancel context.CancelFunc cancel context.CancelFunc
} }
func (w *watcher) handle(_ uint64, data interface{}) { func (w *watcher) handle(idx uint64, data interface{}) {
if data == nil { if data == nil {
return return
} }

@ -4,11 +4,10 @@
import ( import (
"log" "log"
clientv3 "go.etcd.io/etcd/client/v3"
"google.golang.org/grpc"
cfg "github.com/go-kratos/kratos/contrib/config/etcd/v2" cfg "github.com/go-kratos/kratos/contrib/config/etcd/v2"
"github.com/go-kratos/kratos/v2/config" "github.com/go-kratos/kratos/v2/config"
clientv3 "go.etcd.io/etcd/client/v3"
"google.golang.org/grpc"
) )
// create an etcd client // create an etcd client

@ -3,9 +3,9 @@ module github.com/go-kratos/kratos/contrib/config/etcd/v2
go 1.16 go 1.16
require ( require (
github.com/go-kratos/kratos/v2 v2.6.3 github.com/go-kratos/kratos/v2 v2.4.0
go.etcd.io/etcd/client/v3 v3.5.8 go.etcd.io/etcd/client/v3 v3.5.4
google.golang.org/grpc v1.56.1 google.golang.org/grpc v1.46.2
) )
replace github.com/go-kratos/kratos/v2 => ../../../ replace github.com/go-kratos/kratos/v2 => ../../../

File diff suppressed because it is too large Load Diff

@ -8,14 +8,14 @@ import (
"strings" "strings"
"testing" "testing"
"github.com/go-kratos/kratos/v2/config"
v1 "k8s.io/api/core/v1" v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes" "k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest" "k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd" "k8s.io/client-go/tools/clientcmd"
"k8s.io/client-go/util/homedir" "k8s.io/client-go/util/homedir"
"github.com/go-kratos/kratos/v2/config"
) )
const ( const (

@ -3,10 +3,10 @@ module github.com/go-kratos/kratos/contrib/config/kubernetes/v2
go 1.16 go 1.16
require ( require (
github.com/go-kratos/kratos/v2 v2.6.3 github.com/go-kratos/kratos/v2 v2.4.0
k8s.io/api v0.26.3 k8s.io/api v0.24.3
k8s.io/apimachinery v0.26.3 k8s.io/apimachinery v0.24.3
k8s.io/client-go v0.26.3 k8s.io/client-go v0.24.3
) )
replace github.com/go-kratos/kratos/v2 => ../../../ replace github.com/go-kratos/kratos/v2 => ../../../

File diff suppressed because it is too large Load Diff

@ -2,10 +2,9 @@
```go ```go
import ( import (
kconfig "github.com/go-kratos/kratos/v2/config"
"github.com/nacos-group/nacos-sdk-go/clients" "github.com/nacos-group/nacos-sdk-go/clients"
"github.com/nacos-group/nacos-sdk-go/common/constant" "github.com/nacos-group/nacos-sdk-go/common/constant"
kconfig "github.com/go-kratos/kratos/v2/config"
) )

@ -3,8 +3,9 @@ module github.com/go-kratos/kratos/contrib/config/nacos/v2
go 1.16 go 1.16
require ( require (
github.com/go-kratos/kratos/v2 v2.6.3 github.com/go-kratos/kratos/v2 v2.4.0
github.com/nacos-group/nacos-sdk-go v1.0.9 github.com/nacos-group/nacos-sdk-go v1.0.9
gopkg.in/yaml.v3 v3.0.1 // indirect
) )
replace github.com/go-kratos/kratos/v2 => ../../../ replace github.com/go-kratos/kratos/v2 => ../../../

File diff suppressed because it is too large Load Diff

@ -2,23 +2,24 @@
```go ```go
import ( import (
"log" "log"
"github.com/polarismesh/polaris-go" "github.com/polarismesh/polaris-go"
"github.com/go-kratos/kratos/contrib/config/polaris/v2"
"github.com/go-kratos/kratos/contrib/config/polaris/v2"
) )
func main() { func main() {
configApi, err := polaris.NewConfigAPI()
if err != nil {
log.Fatalln(err)
}
source, err := New(&configApi, WithNamespace("default"), WithFileGroup("default"), WithFileName("default.yaml")) configApi, err := polaris.NewConfigAPI()
if err != nil { if err != nil {
log.Fatalln(err) log.Fatalln(err)
} }
source.Load()
source, err := New(&configApi, WithNamespace("default"), WithFileGroup("default"), WithFileName("default.yaml"))
if err != nil {
log.Fatalln(err)
}
source.Load()
} }
``` ```

@ -6,9 +6,9 @@ import (
"path/filepath" "path/filepath"
"strings" "strings"
"github.com/polarismesh/polaris-go"
"github.com/go-kratos/kratos/v2/config" "github.com/go-kratos/kratos/v2/config"
"github.com/polarismesh/polaris-go"
) )
// Option is polaris config option. // Option is polaris config option.

@ -1,28 +1,26 @@
package polaris package config
import ( import (
"encoding/json" "encoding/json"
"errors"
"fmt" "fmt"
"io" "io"
"net/http" "net/http"
"reflect" "reflect"
"strings" "strings"
"testing" "testing"
"time"
"github.com/go-kratos/kratos/v2/config"
"github.com/polarismesh/polaris-go" "github.com/polarismesh/polaris-go"
) )
var ( var (
testNamespace = "default" namespace = "default"
testFileGroup = "test" fileGroup = "test"
testOriginContent = `server: originContent = `server:
port: 8080` port: 8080`
testUpdatedContent = `server: updatedContent = `server:
port: 8090` port: 8090`
testCenterURL = "http://127.0.0.1:8090" configCenterURL = "http://127.0.0.1:8090"
) )
func makeJSONRequest(uri string, data string, method string, headers map[string]string) ([]byte, error) { func makeJSONRequest(uri string, data string, method string, headers map[string]string) ([]byte, error) {
@ -59,7 +57,7 @@ type configClient struct {
} }
func newConfigClient() (*configClient, error) { func newConfigClient() (*configClient, error) {
token, err := getToken(testCenterURL) token, err := getToken()
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -68,7 +66,7 @@ func newConfigClient() (*configClient, error) {
}, nil }, nil
} }
func getToken(testCenterURL string) (string, error) { func getToken() (string, error) {
data, err := json.Marshal(map[string]string{ data, err := json.Marshal(map[string]string{
"name": "polaris", "name": "polaris",
"password": "polaris", "password": "polaris",
@ -77,7 +75,7 @@ func getToken(testCenterURL string) (string, error) {
return "", err return "", err
} }
// login use default user // login use default user
res, err := makeJSONRequest(fmt.Sprintf("%s/core/v1/user/login", testCenterURL), string(data), http.MethodPost, map[string]string{}) res, err := makeJSONRequest(fmt.Sprintf("%s/core/v1/user/login", configCenterURL), string(data), http.MethodPost, map[string]string{})
if err != nil { if err != nil {
return "", nil return "", nil
} }
@ -91,16 +89,16 @@ func getToken(testCenterURL string) (string, error) {
func (client *configClient) createConfigFile(name string) error { func (client *configClient) createConfigFile(name string) error {
data, err := json.Marshal(map[string]string{ data, err := json.Marshal(map[string]string{
"name": name, "name": name,
"namespace": testNamespace, "namespace": namespace,
"group": testFileGroup, "group": fileGroup,
"content": testOriginContent, "content": originContent,
"modifyBy": "polaris", "modifyBy": "polaris",
"format": "yaml", "format": "yaml",
}) })
if err != nil { if err != nil {
return err return err
} }
res, err := makeJSONRequest(fmt.Sprintf("%s/config/v1/configfiles", testCenterURL), string(data), http.MethodPost, map[string]string{ res, err := makeJSONRequest(fmt.Sprintf("%s/config/v1/configfiles", configCenterURL), string(data), http.MethodPost, map[string]string{
"X-Polaris-Token": client.token, "X-Polaris-Token": client.token,
}) })
if err != nil { if err != nil {
@ -113,7 +111,7 @@ func (client *configClient) createConfigFile(name string) error {
return err return err
} }
if resJSON.Code != 200000 { if resJSON.Code != 200000 {
return fmt.Errorf("create error, res: %s", string(res)) return errors.New("create error")
} }
return nil return nil
} }
@ -121,16 +119,16 @@ func (client *configClient) createConfigFile(name string) error {
func (client *configClient) updateConfigFile(name string) error { func (client *configClient) updateConfigFile(name string) error {
data, err := json.Marshal(map[string]string{ data, err := json.Marshal(map[string]string{
"name": name, "name": name,
"namespace": testNamespace, "namespace": namespace,
"group": testFileGroup, "group": fileGroup,
"content": testUpdatedContent, "content": updatedContent,
"modifyBy": "polaris", "modifyBy": "polaris",
"format": "yaml", "format": "yaml",
}) })
if err != nil { if err != nil {
return err return err
} }
res, err := makeJSONRequest(fmt.Sprintf("%s/config/v1/configfiles", testCenterURL), string(data), http.MethodPut, map[string]string{ res, err := makeJSONRequest(fmt.Sprintf("%s/config/v1/configfiles", configCenterURL), string(data), http.MethodPut, map[string]string{
"X-Polaris-Token": client.token, "X-Polaris-Token": client.token,
}) })
if err != nil { if err != nil {
@ -142,7 +140,7 @@ func (client *configClient) updateConfigFile(name string) error {
return err return err
} }
if resJSON.Code != 200000 { if resJSON.Code != 200000 {
return fmt.Errorf("update error, res: %s", string(res)) return errors.New("update error")
} }
return nil return nil
} }
@ -152,7 +150,7 @@ func (client *configClient) deleteConfigFile(name string) error {
if err != nil { if err != nil {
return err return err
} }
url := fmt.Sprintf("%s/config/v1/configfiles?namespace=%s&group=%s&name=%s", testCenterURL, testNamespace, testFileGroup, name) url := fmt.Sprintf("%s/config/v1/configfiles?namespace=%s&group=%s&name=%s", configCenterURL, namespace, fileGroup, name)
res, err := makeJSONRequest(url, string(data), http.MethodDelete, map[string]string{ res, err := makeJSONRequest(url, string(data), http.MethodDelete, map[string]string{
"X-Polaris-Token": client.token, "X-Polaris-Token": client.token,
}) })
@ -165,22 +163,22 @@ func (client *configClient) deleteConfigFile(name string) error {
return err return err
} }
if resJSON.Code != 200000 { if resJSON.Code != 200000 {
return fmt.Errorf("delete error, res: %s", string(res)) return errors.New("delete error")
} }
return nil return nil
} }
func (client *configClient) publishConfigFile(name string) error { func (client *configClient) publishConfigFile(name string) error {
data, err := json.Marshal(map[string]string{ data, err := json.Marshal(map[string]string{
"namespace": testNamespace, "namespace": namespace,
"group": testFileGroup, "group": fileGroup,
"fileName": name, "fileName": name,
"name": name, "name": name,
}) })
if err != nil { if err != nil {
return err return err
} }
res, err := makeJSONRequest(fmt.Sprintf("%s/config/v1/configfiles/release", testCenterURL), string(data), http.MethodPost, map[string]string{ res, err := makeJSONRequest(fmt.Sprintf("%s/config/v1/configfiles/release", configCenterURL), string(data), http.MethodPost, map[string]string{
"X-Polaris-Token": client.token, "X-Polaris-Token": client.token,
}) })
if err != nil { if err != nil {
@ -192,35 +190,30 @@ func (client *configClient) publishConfigFile(name string) error {
return err return err
} }
if resJSON.Code != 200000 { if resJSON.Code != 200000 {
return fmt.Errorf("publish error, res: %s", string(res)) return errors.New("publish error")
} }
return nil return nil
} }
func TestConfig(t *testing.T) { func TestConfig(t *testing.T) {
name := "kratos-polaris-test.yaml" name := "test.yaml"
client, err := newConfigClient() client, err := newConfigClient()
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
_ = client.deleteConfigFile(name)
if err = client.createConfigFile(name); err != nil { if err = client.createConfigFile(name); err != nil {
t.Fatal(err) t.Fatal(err)
} }
time.Sleep(5 * time.Second)
if err = client.publishConfigFile(name); err != nil { if err = client.publishConfigFile(name); err != nil {
t.Fatal(err) t.Fatal(err)
} }
time.Sleep(5 * time.Second)
// Always remember clear test resource // Always remember clear test resource
sdk, err := polaris.NewSDKContextByAddress("127.0.0.1:8091") configAPI, err := polaris.NewConfigAPI()
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
p := New(sdk) config, err := New(configAPI, WithNamespace(namespace), WithFileGroup(fileGroup), WithFileName(name))
config, err := p.Config(WithConfigFile(File{Name: name, Group: testFileGroup}))
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -229,10 +222,7 @@ func TestConfig(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
for _, value := range kv { if len(kv) != 1 || kv[0].Key != name || string(kv[0].Value) != originContent {
t.Logf("key: %s, value: %s", value.Key, value.Value)
}
if len(kv) != 1 || kv[0].Key != name || string(kv[0].Value) != testOriginContent {
t.Fatal("config error") t.Fatal("config error")
} }
@ -240,13 +230,18 @@ func TestConfig(t *testing.T) {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
defer func() {
t.Cleanup(func() {
err = client.deleteConfigFile(name) err = client.deleteConfigFile(name)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
}) if _, err = w.Next(); err != nil {
t.Fatal(err)
}
if err = w.Stop(); err != nil {
t.Fatal(err)
}
}()
if err = client.updateConfigFile(name); err != nil { if err = client.updateConfigFile(name); err != nil {
t.Fatal(err) t.Fatal(err)
@ -260,22 +255,17 @@ func TestConfig(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
for _, value := range kv { if len(kv) != 1 || kv[0].Key != name || string(kv[0].Value) != updatedContent {
t.Log(value.Key, string(value.Value))
}
if len(kv) != 1 || kv[0].Key != name || string(kv[0].Value) != testUpdatedContent {
t.Fatal("config error") t.Fatal("config error")
} }
} }
func TestExtToFormat(t *testing.T) { func TestExtToFormat(t *testing.T) {
name := "kratos-polaris-ext.yaml" name := "ext.yaml"
client, err := newConfigClient() client, err := newConfigClient()
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
_ = client.deleteConfigFile(name)
if err = client.createConfigFile(name); err != nil { if err = client.createConfigFile(name); err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -284,24 +274,22 @@ func TestExtToFormat(t *testing.T) {
} }
// Always remember clear test resource // Always remember clear test resource
t.Cleanup(func() { defer func() {
if err = client.deleteConfigFile(name); err != nil { if err = client.deleteConfigFile(name); err != nil {
t.Fatal(err) t.Fatal(err)
} }
}) }()
sdk, err := polaris.NewSDKContextByAddress("127.0.0.1:8091") configAPI, err := polaris.NewConfigAPI()
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
p := New(sdk)
cfg, err := p.Config(WithConfigFile(File{Name: name, Group: testFileGroup})) config, err := New(configAPI, WithNamespace(namespace), WithFileGroup(fileGroup), WithFileName(name))
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
kv, err := config.Load()
kv, err := cfg.Load()
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -311,76 +299,10 @@ func TestExtToFormat(t *testing.T) {
if !reflect.DeepEqual(name, kv[0].Key) { if !reflect.DeepEqual(name, kv[0].Key) {
t.Errorf("kvs[0].Key is %s", kv[0].Key) t.Errorf("kvs[0].Key is %s", kv[0].Key)
} }
if !reflect.DeepEqual(testOriginContent, string(kv[0].Value)) { if !reflect.DeepEqual(originContent, string(kv[0].Value)) {
t.Errorf("kvs[0].Value is %s", kv[0].Value) t.Errorf("kvs[0].Value is %s", kv[0].Value)
} }
if !reflect.DeepEqual("yaml", kv[0].Format) { if !reflect.DeepEqual("yaml", kv[0].Format) {
t.Errorf("kvs[0].Format is %s", kv[0].Format) t.Errorf("kvs[0].Format is %s", kv[0].Format)
} }
} }
func TestGetMultipleConfig(t *testing.T) {
client, err := newConfigClient()
files := make([]File, 0, 3)
for i := 0; i < 3; i++ {
name := fmt.Sprintf("kratos-polaris-test-%d.yaml", i)
if err != nil {
t.Fatal(err)
}
_ = client.deleteConfigFile(name)
if err = client.createConfigFile(name); err != nil {
t.Fatal(err)
}
if err = client.publishConfigFile(name); err != nil {
t.Fatal(err)
}
files = append(files, File{Name: name, Group: testFileGroup})
}
sdk, err := polaris.NewSDKContextByAddress("127.0.0.1:8091")
if err != nil {
t.Fatal(err)
}
p := New(sdk, WithNamespace("default"))
cfg, err := p.Config(WithConfigFile(files...))
if err != nil {
t.Fatal(err)
}
kvs, err := cfg.Load()
if err != nil {
t.Fatal(err)
}
for _, kv := range kvs {
t.Logf("key: %s, value: %s", kv.Key, kv.Value)
}
w, err := cfg.Watch()
if err != nil {
t.Fatal(err)
}
for _, file := range files {
if err = client.publishConfigFile(file.Name); err != nil {
t.Fatal(err)
}
kvs, err := w.Next()
if err != nil {
t.Fatal(err)
}
m := make(map[string]*config.KeyValue)
for _, kv := range kvs {
m[kv.Key] = kv
}
if !reflect.DeepEqual(file.Name, m[file.Name].Key) {
t.Errorf("m[file.Name].Key is %s", m[file.Name].Key)
}
if !reflect.DeepEqual(testOriginContent, string(m[file.Name].Value)) {
t.Errorf("m[file.Name].Value is %s", m[file.Name].Value)
}
if !reflect.DeepEqual("yaml", m[file.Name].Format) {
t.Errorf("m[file.Name].Format is %s", m[file.Name].Format)
}
}
}

@ -3,7 +3,7 @@ module github.com/go-kratos/kratos/contrib/config/polaris/v2
go 1.16 go 1.16
require ( require (
github.com/go-kratos/kratos/v2 v2.6.3 github.com/go-kratos/kratos/v2 v2.4.0
github.com/polarismesh/polaris-go v1.1.0 github.com/polarismesh/polaris-go v1.1.0
) )
@ -14,6 +14,10 @@ require (
go.uber.org/atomic v1.9.0 // indirect go.uber.org/atomic v1.9.0 // indirect
go.uber.org/multierr v1.8.0 // indirect go.uber.org/multierr v1.8.0 // indirect
go.uber.org/zap v1.21.0 // indirect go.uber.org/zap v1.21.0 // indirect
golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e // indirect
golang.org/x/sys v0.0.0-20220627191245-f75cf1eec38b // indirect
google.golang.org/genproto v0.0.0-20220628213854-d9e0b6570c03 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
) )
replace github.com/go-kratos/kratos/v2 => ../../../ replace github.com/go-kratos/kratos/v2 => ../../../

File diff suppressed because it is too large Load Diff

@ -5,11 +5,11 @@ import (
"path/filepath" "path/filepath"
"strings" "strings"
"github.com/polarismesh/polaris-go"
"github.com/polarismesh/polaris-go/pkg/model"
"github.com/go-kratos/kratos/v2/config" "github.com/go-kratos/kratos/v2/config"
"github.com/go-kratos/kratos/v2/log" "github.com/go-kratos/kratos/v2/log"
"github.com/polarismesh/polaris-go"
"github.com/polarismesh/polaris-go/pkg/model"
) )
type Watcher struct { type Watcher struct {
@ -28,7 +28,7 @@ func getFullPath(namespace string, fileGroup string, fileName string) string {
return fmt.Sprintf("%s/%s/%s", namespace, fileGroup, fileName) return fmt.Sprintf("%s/%s/%s", namespace, fileGroup, fileName)
} }
func receive(event model.ConfigFileChangeEvent) { func recieve(event model.ConfigFileChangeEvent) {
meta := event.ConfigFileMetadata meta := event.ConfigFileMetadata
ec := eventChanMap[getFullPath(meta.GetNamespace(), meta.GetFileGroup(), meta.GetFileName())] ec := eventChanMap[getFullPath(meta.GetNamespace(), meta.GetFileGroup(), meta.GetFileName())]
defer func() { defer func() {
@ -42,7 +42,7 @@ func receive(event model.ConfigFileChangeEvent) {
} }
func newWatcher(configFile polaris.ConfigFile) *Watcher { func newWatcher(configFile polaris.ConfigFile) *Watcher {
configFile.AddChangeListener(receive) configFile.AddChangeListener(recieve)
fullPath := getFullPath(configFile.GetNamespace(), configFile.GetFileGroup(), configFile.GetFileName()) fullPath := getFullPath(configFile.GetNamespace(), configFile.GetFileGroup(), configFile.GetFileName())
if _, ok := eventChanMap[fullPath]; !ok { if _, ok := eventChanMap[fullPath]; !ok {

@ -3,7 +3,7 @@ module github.com/go-kratos/kratos/contrib/encoding/msgpack/v2
go 1.16 go 1.16
require ( require (
github.com/go-kratos/kratos/v2 v2.6.3 github.com/go-kratos/kratos/v2 v2.5.2
github.com/vmihailenco/msgpack/v5 v5.3.5 github.com/vmihailenco/msgpack/v5 v5.3.5
) )

File diff suppressed because it is too large Load Diff

@ -58,7 +58,7 @@ func TestWithAccessSecret(t *testing.T) {
} }
} }
func TestLogger(_ *testing.T) { func TestLogger(t *testing.T) {
project := "foo" project := "foo"
logger := NewAliyunLog(WithProject(project)) logger := NewAliyunLog(WithProject(project))
defer logger.Close() defer logger.Close()
@ -116,22 +116,21 @@ func TestToString(t *testing.T) {
{"float64", 6.66, "6.66"}, {"float64", 6.66, "6.66"},
{"max float64", math.MaxFloat64, "179769313486231570000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"}, //nolint:lll {"max float64", math.MaxFloat64, "179769313486231570000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"}, //nolint:lll
{"float32", float32(6.66), "6.66"}, {"float32", float32(6.66), "6.66"},
{"max float32", float32(math.MaxFloat32), "340282350000000000000000000000000000000"}, {"max float32", math.MaxFloat32, "340282346638528860000000000000000000000"},
{"int", math.MaxInt64, "9223372036854775807"}, {"int", int(math.MaxInt64), "9223372036854775807"},
{"uint", uint(math.MaxUint64), "18446744073709551615"}, {"uint", uint(math.MaxUint64), "18446744073709551615"},
{"int8", int8(math.MaxInt8), "127"}, {"int8", math.MaxInt8, "127"},
{"uint8", uint8(math.MaxUint8), "255"}, {"uint8", math.MaxUint8, "255"},
{"int16", int16(math.MaxInt16), "32767"}, {"int16", math.MaxInt16, "32767"},
{"uint16", uint16(math.MaxUint16), "65535"}, {"uint16", math.MaxUint16, "65535"},
{"int32", int32(math.MaxInt32), "2147483647"}, {"int32", math.MaxInt32, "2147483647"},
{"uint32", uint32(math.MaxUint32), "4294967295"}, {"uint32", math.MaxUint32, "4294967295"},
{"int64", int64(math.MaxInt64), "9223372036854775807"}, {"int64", math.MaxInt64, "9223372036854775807"},
{"uint64", uint64(math.MaxUint64), "18446744073709551615"}, {"uint64", uint64(math.MaxUint64), "18446744073709551615"},
{"string", "abc", "abc"}, {"string", "abc", "abc"},
{"bool", false, "false"}, {"bool", false, "false"},
{"[]byte", []byte("abc"), "abc"}, {"[]byte", []byte("abc"), "abc"},
{"struct", struct{ Name string }{}, `{"Name":""}`}, {"struct", struct{ Name string }{}, `{"Name":""}`},
{"nil", nil, ""},
} }
for _, test := range tests { for _, test := range tests {
t.Run(test.name, func(t *testing.T) { t.Run(test.name, func(t *testing.T) {

@ -3,9 +3,9 @@ module github.com/go-kratos/kratos/contrib/log/aliyun/v2
go 1.16 go 1.16
require ( require (
github.com/aliyun/aliyun-log-go-sdk v0.1.44 github.com/aliyun/aliyun-log-go-sdk v0.1.37
github.com/go-kratos/kratos/v2 v2.6.3 github.com/go-kratos/kratos/v2 v2.5.2
google.golang.org/protobuf v1.31.0 google.golang.org/protobuf v1.28.1
) )
replace ( replace (

File diff suppressed because it is too large Load Diff

@ -5,7 +5,8 @@ go 1.16
require ( require (
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 // indirect github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 // indirect
github.com/fluent/fluent-logger-golang v1.9.0 github.com/fluent/fluent-logger-golang v1.9.0
github.com/go-kratos/kratos/v2 v2.6.3 github.com/go-kratos/kratos/v2 v2.5.2
github.com/kr/pretty v0.3.0 // indirect
github.com/tinylib/msgp v1.1.6 // indirect github.com/tinylib/msgp v1.1.6 // indirect
) )

File diff suppressed because it is too large Load Diff

@ -3,7 +3,7 @@ module github.com/go-kratos/kratos/contrib/log/logrus/v2
go 1.16 go 1.16
require ( require (
github.com/go-kratos/kratos/v2 v2.6.3 github.com/go-kratos/kratos/v2 v2.5.2
github.com/sirupsen/logrus v1.8.1 github.com/sirupsen/logrus v1.8.1
) )

File diff suppressed because it is too large Load Diff

@ -3,9 +3,9 @@ module github.com/go-kratos/kratos/contrib/log/tencent/v2
go 1.16 go 1.16
require ( require (
github.com/go-kratos/kratos/v2 v2.6.3 github.com/go-kratos/kratos/v2 v2.5.2
github.com/tencentcloud/tencentcloud-cls-sdk-go v1.0.2 github.com/tencentcloud/tencentcloud-cls-sdk-go v1.0.2
google.golang.org/protobuf v1.31.0 google.golang.org/protobuf v1.28.0
) )
replace github.com/go-kratos/kratos/v2 => ../../../ replace github.com/go-kratos/kratos/v2 => ../../../

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

Loading…
Cancel
Save