pull/708/head
chenzhihui 4 years ago
parent 3daca16dc5
commit 3566386a89
  1. 3
      .github/FUNDING.yml
  2. 131
      .github/workflows/go.yml
  3. 52
      .gitignore
  4. 138
      .golangci.yml
  5. 59
      .travis.yml
  6. 2
      LICENSE
  7. 119
      README.md
  8. 1
      api/README.md
  9. 100
      api/kratos/api/annotations.pb.go
  10. 13
      api/kratos/api/annotations.proto
  11. 134
      app.go
  12. 25
      app_test.go
  13. 10
      cmd/kratos/go.mod
  14. 354
      cmd/kratos/go.sum
  15. 16
      cmd/kratos/internal/base/mod.go
  16. 100
      cmd/kratos/internal/base/path.go
  17. 71
      cmd/kratos/internal/base/repo.go
  18. 16
      cmd/kratos/internal/base/repo_test.go
  19. 36
      cmd/kratos/internal/new/new.go
  20. 38
      cmd/kratos/internal/new/project.go
  21. 65
      cmd/kratos/internal/proto/add/add.go
  22. 41
      cmd/kratos/internal/proto/add/proto.go
  23. 52
      cmd/kratos/internal/proto/add/template.go
  24. 27
      cmd/kratos/internal/proto/proto.go
  25. 86
      cmd/kratos/internal/proto/service/service.go
  26. 56
      cmd/kratos/internal/proto/service/template.go
  27. 28
      cmd/kratos/internal/proto/source/source.go
  28. 27
      cmd/kratos/main.go
  29. 13
      cmd/kratos/version.go
  30. 58
      cmd/protoc-gen-go-errors/errors.go
  31. 9
      cmd/protoc-gen-go-errors/go.mod
  32. 101
      cmd/protoc-gen-go-errors/go.sum
  33. 35
      cmd/protoc-gen-go-errors/main.go
  34. 40
      cmd/protoc-gen-go-errors/template.go
  35. 11
      cmd/protoc-gen-go-http/go.mod
  36. 99
      cmd/protoc-gen-go-http/go.sum
  37. 210
      cmd/protoc-gen-go-http/http.go
  38. 669
      cmd/protoc-gen-go-http/internal/testproto/echo_service.pb.go
  39. 101
      cmd/protoc-gen-go-http/internal/testproto/echo_service.proto
  40. 219
      cmd/protoc-gen-go-http/internal/testproto/echo_service_grpc.pb.go
  41. 240
      cmd/protoc-gen-go-http/internal/testproto/echo_service_http.pb.go
  42. 35
      cmd/protoc-gen-go-http/main.go
  43. 101
      cmd/protoc-gen-go-http/template.go
  44. 37
      config/README.md
  45. 141
      config/config.go
  46. 79
      config/file/file.go
  47. 142
      config/file/file_test.go
  48. 53
      config/file/watcher.go
  49. 38
      config/options.go
  50. 107
      config/reader.go
  51. 20
      config/source.go
  52. 105
      config/value.go
  53. 0
      docs/.nojekyll
  54. 1
      docs/CNAME
  55. 21
      docs/FAQ.md
  56. 36
      docs/README.md
  57. 39
      docs/_sidebar.md
  58. 177
      docs/blademaster-mid.md
  59. 88
      docs/blademaster-mod.md
  60. 83
      docs/blademaster-pb.md
  61. 142
      docs/blademaster-quickstart.md
  62. 43
      docs/blademaster.md
  63. 49
      docs/breaker.md
  64. 195
      docs/cache-mc.md
  65. 181
      docs/cache-redis.md
  66. 20
      docs/cache.md
  67. 118
      docs/config-paladin.md
  68. 48
      docs/config.md
  69. 51
      docs/database-hbase.md
  70. 42
      docs/database-mysql-orm.md
  71. 195
      docs/database-mysql.md
  72. 0
      docs/database-tidb.md
  73. 19
      docs/database.md
  74. 14
      docs/design/kratos-v2.md
  75. 102
      docs/ecode.md
  76. BIN
      docs/images/alipay.png
  77. 0
      docs/images/kratos.png
  78. BIN
      docs/img/bm-arch-2-2.png
  79. BIN
      docs/img/bm-arch-2-3.png
  80. BIN
      docs/img/bm-handlers.png
  81. BIN
      docs/img/kratos-log.jpg
  82. BIN
      docs/img/kratos.png
  83. BIN
      docs/img/kratos2.png
  84. BIN
      docs/img/ratelimit-benchmark-up-1.png
  85. BIN
      docs/img/ratelimit-rolling-window.png
  86. BIN
      docs/img/zipkin.jpg
  87. 30
      docs/index.html
  88. 66
      docs/install.md
  89. 27
      docs/kratos-genbts.md
  90. 68
      docs/kratos-genmc.md
  91. 28
      docs/kratos-protoc.md
  92. 8
      docs/kratos-swagger.md
  93. 106
      docs/kratos-tool.md
  94. 0
      docs/log-agent.md
  95. 34
      docs/logger.md
  96. 26
      docs/protoc.md
  97. 71
      docs/quickstart.md
  98. 57
      docs/ratelimit.md
  99. 42
      docs/trace.md
  100. 500
      docs/ut-support.md
  101. Some files were not shown because too many files have changed in this diff Show More

@ -0,0 +1,3 @@
# These are supported funding model platforms
github: [tonybase]

@ -1,131 +0,0 @@
name: Go
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
jobs:
build:
name: Build on ${{ matrix.os }} - Go${{ matrix.go_version }}
runs-on: ${{ matrix.os }}
strategy:
matrix:
go_version:
- 1.13
os:
- ubuntu-latest
steps:
- name: Set up Go ${{ matrix.go_version }}
uses: actions/setup-go@v1
with:
go-version: ${{ matrix.go_version }}
id: go
- name: Set up Env
run: |
echo "GOPATH=$(go env GOPATH)" >> $GITHUB_ENV
echo "$(go env GOPATH)/bin" >> $GITHUB_PATH
- name: Check out code into the Go module directory
uses: actions/checkout@v2
- name: Cache dependencies
uses: actions/cache@v2
with:
# Cache
path: ~/go/pkg/mod
# Cache key
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
# An ordered list of keys to use for restoring the cache if no cache hit occurred for key
restore-keys: |
${{ runner.os }}-go-
- name: Get dependencies
run: |
go get -v -t -d ./...
if [ -f Gopkg.toml ]; then
curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh
dep ensure
fi
- name: Build
run: go build ./...
- name: Golangci
run: |
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.33.0
golangci-lint run --out-format=github-actions
- name: Test
run: go test ./... -coverprofile=coverage.txt -covermode=atomic
- name: Coverage
run: bash <(curl -s https://codecov.io/bash)
scaffold:
name: Scaffold Test on ${{ matrix.os }} - Go${{ matrix.go_version }}
runs-on: ${{ matrix.os }}
strategy:
matrix:
go_version:
- 1.13
os:
- ubuntu-latest
steps:
- name: Set up Go ${{ matrix.go_version }}
uses: actions/setup-go@v1
with:
go-version: ${{ matrix.go_version }}
id: go
- name: Set up Env
run: |
echo "GOPATH=$(go env GOPATH)" >> $GITHUB_ENV
echo "$(go env GOPATH)/bin" >> $GITHUB_PATH
- name: Check out code into the Go module directory
uses: actions/checkout@v2
- name: Cache dependencies
uses: actions/cache@v2
with:
# Cache
path: ~/go/pkg/mod
# Cache key
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
# An ordered list of keys to use for restoring the cache if no cache hit occurred for key
restore-keys: |
${{ runner.os }}-go-
- name: Get dependencies
run: |
go get -v -t -d ./...
if [ -f Gopkg.toml ]; then
curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh
dep ensure
fi
wget https://github.com/google/protobuf/releases/download/v3.11.4/protoc-3.11.4-linux-x86_64.zip
unzip protoc-3.11.4-linux-x86_64.zip
chmod +x bin/protoc
sudo mv bin/protoc /usr/local/bin
sudo mv include /usr/local/bin
go get -u github.com/golang/protobuf/protoc-gen-go
go get -u github.com/gogo/protobuf/protoc-gen-gofast
- name: Tool
run: |
go install ./...
mkdir -p $GOPATH/src
cp -R ../kratos $GOPATH/src
cd $GOPATH/src
kratos new kratos-demo
cd kratos-demo
go build ./...

52
.gitignore vendored

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

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

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

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

@ -1,4 +1,4 @@
![kratos](docs/img/kratos3.png)
![kratos](docs/images/kratos.png)
[![Language](https://img.shields.io/badge/Language-Go-blue.svg)](https://golang.org/)
[![Build Status](https://github.com/go-kratos/kratos/workflows/Go/badge.svg)](https://github.com/go-kratos/kratos/actions)
@ -8,69 +8,86 @@
# Kratos
Kratos是[bilibili](https://www.bilibili.com)开源的一套Go微服务框架,包含大量微服务相关框架及工具。
Kratos 一套轻量级 Go 微服务框架,包含大量微服务相关框架及工具。
> 名字来源于:《战神》游戏以希腊神话为背景,讲述由凡人成为战神的奎托斯(Kratos)成为战神并展开弑神屠杀的冒险历程。
## Goals
我们致力于提供完整的微服务研发体验,整合相关框架及工具后,微服务治理相关部分可对整体业务开发周期无感,从而更加聚焦于业务交付。对每位开发者而言,整套Kratos框架也是不错的学习仓库,可以了解和参考到[bilibili](https://www.bilibili.com)在微服务方面的技术积累和经验。
我们致力于提供完整的微服务研发体验,整合相关框架及工具后,微服务治理相关部分可对整体业务开发周期无感,从而更加聚焦于业务交付。对每位开发者而言,整套Kratos框架也是不错的学习仓库,可以了解和参考到微服务方面的技术积累和经验。
## Features
* HTTP Blademaster:核心基于[gin](https://github.com/gin-gonic/gin)进行模块化设计,简单易用、核心足够轻量;
* GRPC Warden:基于官方gRPC开发,集成[discovery](https://github.com/bilibili/discovery)服务发现,并融合P2C负载均衡;
* Cache:优雅的接口化设计,非常方便的缓存序列化,推荐结合代理模式[overlord](https://github.com/bilibili/overlord);
* Database:集成MySQL/HBase/TiDB,添加熔断保护和统计支持,可快速发现数据层压力;
* Config:方便易用的[paladin sdk](https://go-kratos.github.io/kratos/#/config),可配合远程配置中心,实现配置版本管理和更新;
* Log:类似[zap](https://github.com/uber-go/zap)的field实现高性能日志库,并结合log-agent实现远程日志管理;
* Trace:基于opentracing,集成了全链路trace支持(gRPC/HTTP/MySQL/Redis/Memcached);
* Kratos Tool:工具链,可快速生成标准项目,或者通过Protobuf生成代码,非常便捷使用gRPC、HTTP、swagger文档;
## Quick start
### Requirments
Go version>=1.13
### Principles
### Installation
```shell
# Linux/macOS
GO111MODULE=on && go get -u github.com/go-kratos/kratos/tool/kratos
* 简单:不过度设计,代码平实简单;
* 通用:通用业务开发所需要的基础库的功能;
* 高效:提高业务迭代的效率;
* 稳定:基础库可测试性高,覆盖率高,有线上实践安全可靠;
* 健壮:通过良好的基础库设计,减少错用;
* 高性能:性能高,但不特定为了性能做hack优化,引入unsafe;
* 扩展性:良好的接口设计,来扩展实现,或者通过新增基础库目录来扩展功能;
* 容错性:为失败设计,大量引入对SRE的理解,鲁棒性高;
* 工具链:包含大量工具链,比如cache代码生成,lint工具等等;
# Windows (Powershell)
go env -w GO111MODULE=on ; go get -u github.com/go-kratos/kratos/tool/kratos
# Windows (CMD)
go env -w GO111MODULE=on && go get -u github.com/go-kratos/kratos/tool/kratos
cd $GOPATH/src
kratos new kratos-demo
## Features
* APIs:协议通信以 HTTP/gRPC 为基础,通过 Protobuf 进行定义;
* Errors:通过 Protobuf 的 Enum 作为错误码定义,以及工具生成判定接口;
* Metadata:在协议通信 HTTP/gRPC 中,通过 Middleware 规范化服务元信息传递;
* Config:通过KeyValue方式实现,对多种配置源进行铺平,以Atomic方式支持动态配置;
* Logger:标准日志接口,可方便集成三方 log 库,并可通过 fluentd 收集日志;
* Metrics:统一指标接口,可以实现各种指标系统,默认集成 Prometheus;
* Tracing:遵循 OpenTracing 规范定义,以实现微服务链路追踪;
* Encoding:支持Accept和Content-Type进行自动选择内容编码;
* Transport:通用的 HTTP/gRPC 传输层,实现统一的 Middleware 插件支持;
* Server:进行基础的 Server 层封装,统一以 Options 方式配置使用;
## Getting Started
### Required
- [go](https://golang.org/dl/)
- [protoc](https://github.com/protocolbuffers/protobuf)
- [protoc-gen-go](https://github.com/protocolbuffers/protobuf-go)
### Install Kratos
```
通过 `kratos new` 会快速生成基于kratos库的脚手架代码,如生成 [kratos-demo](https://github.com/bilibili/kratos-demo)
### Build & Run
```shell
cd kratos-demo/cmd
go build
./cmd -conf ../configs
# 安装生成工具
go get github.com/go-kratos/kratos/cmd/kratos
go get github.com/go-kratos/kratos/cmd/protoc-gen-go-http
go get github.com/go-kratos/kratos/cmd/protoc-gen-go-errors
# 或者通过 Source 安装
cd cmd/kratos && go install
cd cmd/protoc-gen-go-http && go install
cd cmd/protoc-gen-go-errors && go install
```
### Create a service
```
# 创建项目模板
kratos new helloworld
cd helloworld
# 生成proto模板
kratos proto add api/helloworld/helloworld.proto
# 生成service模板
kratos proto service api/helloworld/helloworld.proto -t internal/service
# 生成api下所有proto文件
make proto
# 编码cmd下所有main文件
make build
# 进行单元测试
make test
```
打开浏览器访问:[http://localhost:8000/kratos-demo/start](http://localhost:8000/kratos-demo/start),你会看到输出了`Golang 大法好 !!!`
[快速开始](https://go-kratos.github.io/kratos/#/quickstart) [kratos工具](https://go-kratos.github.io/kratos/#/kratos-tool)
## Service Layout
* [Service Layout](https://github.com/go-kratos/kratos-layout)
## Documentation
## Community
* [Wechat Group](https://github.com/go-kratos/kratos/issues/682)
* [Discord Group](https://discord.gg/BWzJsUJ)
* QQ Group: 716486124
> [简体中文](https://go-kratos.github.io/kratos)
> [简体中文(国内镜像)](https://go-kratos.gitee.io/kratos/)
> [FAQ](https://go-kratos.github.io/kratos/#/FAQ)
## Sponsors and Backers
## 社区
* [官方微信群](https://github.com/go-kratos/kratos/issues/682) (推荐)
* [Discord Group](https://discord.gg/BWzJsUJ)
![kratos](docs/images/alipay.png)
## License
Kratos is under the MIT license. See the [LICENSE](./LICENSE) file for details.
Kratos is MIT licensed. See the [LICENSE](./LICENSE) file for details.

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

@ -0,0 +1,100 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.25.0
// protoc v3.13.0
// source: kratos/api/annotations.proto
package api
import (
proto "github.com/golang/protobuf/proto"
descriptor "github.com/golang/protobuf/protoc-gen-go/descriptor"
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
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)
)
// This is a compile-time assertion that a sufficiently up-to-date version
// of the legacy proto package is being used.
const _ = proto.ProtoPackageIsVersion4
var file_kratos_api_annotations_proto_extTypes = []protoimpl.ExtensionInfo{
{
ExtendedType: (*descriptor.EnumOptions)(nil),
ExtensionType: (*bool)(nil),
Field: 1000,
Name: "kratos.api.errors",
Tag: "varint,1000,opt,name=errors",
Filename: "kratos/api/annotations.proto",
},
}
// Extension fields to descriptor.EnumOptions.
var (
// optional bool errors = 1000;
E_Errors = &file_kratos_api_annotations_proto_extTypes[0]
)
var File_kratos_api_annotations_proto protoreflect.FileDescriptor
var file_kratos_api_annotations_proto_rawDesc = []byte{
0x0a, 0x1c, 0x6b, 0x72, 0x61, 0x74, 0x6f, 0x73, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x61, 0x6e, 0x6e,
0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0a,
0x6b, 0x72, 0x61, 0x74, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x1a, 0x20, 0x67, 0x6f, 0x6f, 0x67,
0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x64, 0x65, 0x73, 0x63,
0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x3a, 0x35, 0x0a, 0x06,
0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x12, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e,
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6e, 0x75, 0x6d, 0x4f, 0x70, 0x74,
0x69, 0x6f, 0x6e, 0x73, 0x18, 0xe8, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x65, 0x72, 0x72,
0x6f, 0x72, 0x73, 0x42, 0x58, 0x0a, 0x15, 0x63, 0x6f, 0x6d, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75,
0x62, 0x2e, 0x6b, 0x72, 0x61, 0x74, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x50, 0x01, 0x5a, 0x31,
0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x6f, 0x2d, 0x6b, 0x72,
0x61, 0x74, 0x6f, 0x73, 0x2f, 0x6b, 0x72, 0x61, 0x74, 0x6f, 0x73, 0x2f, 0x76, 0x32, 0x2f, 0x61,
0x70, 0x69, 0x2f, 0x6b, 0x72, 0x61, 0x74, 0x6f, 0x73, 0x2f, 0x61, 0x70, 0x69, 0x3b, 0x61, 0x70,
0x69, 0xa2, 0x02, 0x09, 0x4b, 0x72, 0x61, 0x74, 0x6f, 0x73, 0x41, 0x50, 0x49, 0x62, 0x06, 0x70,
0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var file_kratos_api_annotations_proto_goTypes = []interface{}{
(*descriptor.EnumOptions)(nil), // 0: google.protobuf.EnumOptions
}
var file_kratos_api_annotations_proto_depIdxs = []int32{
0, // 0: kratos.api.errors:extendee -> google.protobuf.EnumOptions
1, // [1:1] is the sub-list for method output_type
1, // [1:1] is the sub-list for method input_type
1, // [1:1] is the sub-list for extension type_name
0, // [0:1] is the sub-list for extension extendee
0, // [0:0] is the sub-list for field type_name
}
func init() { file_kratos_api_annotations_proto_init() }
func file_kratos_api_annotations_proto_init() {
if File_kratos_api_annotations_proto != nil {
return
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_kratos_api_annotations_proto_rawDesc,
NumEnums: 0,
NumMessages: 0,
NumExtensions: 1,
NumServices: 0,
},
GoTypes: file_kratos_api_annotations_proto_goTypes,
DependencyIndexes: file_kratos_api_annotations_proto_depIdxs,
ExtensionInfos: file_kratos_api_annotations_proto_extTypes,
}.Build()
File_kratos_api_annotations_proto = out.File
file_kratos_api_annotations_proto_rawDesc = nil
file_kratos_api_annotations_proto_goTypes = nil
file_kratos_api_annotations_proto_depIdxs = nil
}

@ -0,0 +1,13 @@
syntax = "proto3";
package kratos.api;
option go_package = "github.com/go-kratos/kratos/v2/api/kratos/api;api";
option java_multiple_files = true;
option java_package = "com.github.kratos.api";
option objc_class_prefix = "KratosAPI";
import "google/protobuf/descriptor.proto";
extend google.protobuf.EnumOptions {
bool errors = 1000;
}

134
app.go

@ -0,0 +1,134 @@
package kratos
import (
"context"
"errors"
"os"
"os/signal"
"syscall"
"github.com/go-kratos/kratos/v2/log"
"github.com/go-kratos/kratos/v2/registry"
"github.com/go-kratos/kratos/v2/transport"
"github.com/google/uuid"
"golang.org/x/sync/errgroup"
)
// App is an application components lifecycle manager
type App struct {
opts options
ctx context.Context
cancel func()
instance *registry.ServiceInstance
log *log.Helper
}
// New create an application lifecycle manager.
func New(opts ...Option) *App {
options := options{
ctx: context.Background(),
logger: log.DefaultLogger,
sigs: []os.Signal{syscall.SIGTERM, syscall.SIGQUIT, syscall.SIGINT},
}
if id, err := uuid.NewUUID(); err == nil {
options.id = id.String()
}
for _, o := range opts {
o(&options)
}
ctx, cancel := context.WithCancel(options.ctx)
return &App{
opts: options,
ctx: ctx,
cancel: cancel,
instance: serviceInstance(options),
log: log.NewHelper("app", options.logger),
}
}
// Logger returns logger.
func (a *App) Logger() log.Logger {
return a.opts.logger
}
// Server returns transport servers.
func (a *App) Server() []transport.Server {
return a.opts.servers
}
// Registry returns registry.
func (a *App) Registry() registry.Registry {
return a.opts.registry
}
// Run executes all OnStart hooks registered with the application's Lifecycle.
func (a *App) Run() error {
a.log.Infow(
"service_id", a.opts.id,
"service_name", a.opts.name,
"version", a.opts.version,
)
g, ctx := errgroup.WithContext(a.ctx)
for _, srv := range a.opts.servers {
srv := srv
g.Go(func() error {
<-ctx.Done() // wait for stop signal
return srv.Stop()
})
g.Go(func() error {
return srv.Start()
})
}
if a.opts.registry != nil {
if err := a.opts.registry.Register(a.instance); err != nil {
return err
}
}
c := make(chan os.Signal, 1)
signal.Notify(c, a.opts.sigs...)
g.Go(func() error {
for {
select {
case <-ctx.Done():
return ctx.Err()
case <-c:
a.Stop()
}
}
})
if err := g.Wait(); err != nil && !errors.Is(err, context.Canceled) {
return err
}
return nil
}
// Stop gracefully stops the application.
func (a *App) Stop() error {
if a.opts.registry != nil {
if err := a.opts.registry.Deregister(a.instance); err != nil {
return err
}
}
if a.cancel != nil {
a.cancel()
}
return nil
}
func serviceInstance(o options) *registry.ServiceInstance {
if len(o.endpoints) == 0 {
for _, srv := range o.servers {
if e, err := srv.Endpoint(); err == nil {
o.endpoints = append(o.endpoints, e)
}
}
}
return &registry.ServiceInstance{
ID: o.id,
Name: o.name,
Version: o.version,
Metadata: o.metadata,
Endpoints: o.endpoints,
}
}

@ -0,0 +1,25 @@
package kratos
import (
"testing"
"time"
"github.com/go-kratos/kratos/v2/transport/grpc"
"github.com/go-kratos/kratos/v2/transport/http"
)
func TestApp(t *testing.T) {
hs := http.NewServer()
gs := grpc.NewServer()
app := New(
Name("kratos"),
Version("v1.0.0"),
Server(hs, gs),
)
time.AfterFunc(time.Second, func() {
app.Stop()
})
if err := app.Run(); err != nil {
t.Fatal(err)
}
}

@ -0,0 +1,10 @@
module github.com/go-kratos/kratos/cmd/kratos
go 1.15
require (
github.com/emicklei/proto v1.9.0
github.com/go-git/go-git/v5 v5.2.0
github.com/spf13/cobra v1.1.1
golang.org/x/mod v0.4.0
)

@ -0,0 +1,354 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk=
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs=
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA=
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
github.com/emicklei/proto v1.9.0 h1:l0QiNT6Qs7Yj0Mb4X6dnWBQer4ebei2BFcgQLbGqUDc=
github.com/emicklei/proto v1.9.0/go.mod h1:rn1FgRS/FANiZdD2djyH7TMA9jdRDcYQ9IEN9yvjX0A=
github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg=
github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 h1:BHsljHzVlRcyQhjrss6TZTdY2VfCqZPbv5k3iBFa2ZQ=
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/gliderlabs/ssh v0.2.2 h1:6zsha5zo/TWhRhwqCD3+EarCAgZ2yN28ipRnGPnwkI0=
github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
github.com/go-git/gcfg v1.5.0 h1:Q5ViNfGF8zFgyJWPqYwA7qGFoMTEiBmdlkcfRmpIMa4=
github.com/go-git/gcfg v1.5.0/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4ua+E=
github.com/go-git/go-billy/v5 v5.0.0 h1:7NQHvd9FVid8VL4qVUMm8XifBK+2xCoZ2lSk0agRrHM=
github.com/go-git/go-billy/v5 v5.0.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0=
github.com/go-git/go-git-fixtures/v4 v4.0.2-0.20200613231340-f56387b50c12 h1:PbKy9zOy4aAKrJ5pibIRpVO2BXnK1Tlcg+caKI7Ox5M=
github.com/go-git/go-git-fixtures/v4 v4.0.2-0.20200613231340-f56387b50c12/go.mod h1:m+ICp2rF3jDhFgEZ/8yziagdT1C+ZpZcrJjappBCDSw=
github.com/go-git/go-git/v5 v5.2.0 h1:YPBLG/3UK1we1ohRkncLjaXWLW+HKp5QNM/jTli2JgI=
github.com/go-git/go-git/v5 v5.2.0/go.mod h1:kh02eMX+wdqqxgNMEyq8YgwlIOsDOa9homkUq1PoTMs=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
github.com/imdario/mergo v0.3.9 h1:UauaLniWCFHWd+Jp9oCEkTBj8VO/9DKg3PV3VCNMDIg=
github.com/imdario/mergo v0.3.9/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd h1:Coekwdh0v2wtGp9Gmz1Ze3eVRAWJMLokvN3QjdzCHLY=
github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cobra v1.1.1 h1:KfztREH0tPxJJ+geloSLaAkaPkr4ki2Er5quFV1TDo4=
github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI=
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/xanzy/ssh-agent v0.2.1 h1:TCbipTQL2JiiCprBWx9frJ2eJlCYT00NmctrHxVAr70=
github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073 h1:xMPOj6Pz6UipU1wXLkrtqpHbR0AVFnyPEQq/wRWz9lM=
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/mod v0.1.0 h1:sfUMP1Gu8qASkorDVjnMuvgJzwFbTZSeXFiGBYAVdl4=
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
golang.org/x/mod v0.4.0 h1:8pl+sMODzuvGJkmj2W4kZihvVb5mKm8pB/X44PIQHv8=
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200301022130-244492dfa37a h1:GuSPYbZzB5/dcLNCwLQLsg3obCJtX9IJhpXkvY7kzk0=
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190221075227-b4e8571b14e0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527 h1:uYVVQ9WP/Ds2ROhcaGPeIdVq0RIXVLwsHlnvJ+cT1So=
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/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-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898 h1:/atklqdjdhuosWIl6AIbOeHJjicWYPqR9bpxqxYG2pA=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=

@ -0,0 +1,16 @@
package base
import (
"io/ioutil"
"golang.org/x/mod/modfile"
)
// ModulePath returns go module path.
func ModulePath(filename string) (string, error) {
modBytes, err := ioutil.ReadFile(filename)
if err != nil {
return "", err
}
return modfile.ModulePath(modBytes), nil
}

@ -0,0 +1,100 @@
package base
import (
"bytes"
"io/ioutil"
"log"
"os"
"path"
)
func kratosHome() string {
dir, err := os.UserHomeDir()
if err != nil {
log.Fatal(err)
}
home := path.Join(dir, ".kratos")
if _, err := os.Stat(home); os.IsNotExist(err) {
if err := os.MkdirAll(home, 0700); err != nil {
log.Fatal(err)
}
}
return home
}
func kratosHomeWithDir(dir string) string {
home := path.Join(kratosHome(), dir)
if _, err := os.Stat(home); os.IsNotExist(err) {
if err := os.MkdirAll(home, 0700); err != nil {
log.Fatal(err)
}
}
return home
}
func copyFile(src, dst string, replaces []string) error {
var err error
srcinfo, err := os.Stat(src)
if err != nil {
return err
}
buf, err := ioutil.ReadFile(src)
if err != nil {
return err
}
var old string
for i, next := range replaces {
if i%2 == 0 {
old = next
continue
}
buf = bytes.ReplaceAll(buf, []byte(old), []byte(next))
}
return ioutil.WriteFile(dst, buf, srcinfo.Mode())
}
func copyDir(src, dst string, replaces, ignores []string) error {
var err error
var fds []os.FileInfo
var srcinfo os.FileInfo
if srcinfo, err = os.Stat(src); err != nil {
return err
}
if err = os.MkdirAll(dst, srcinfo.Mode()); err != nil {
return err
}
if fds, err = ioutil.ReadDir(src); err != nil {
return err
}
for _, fd := range fds {
if hasSets(fd.Name(), ignores) {
continue
}
srcfp := path.Join(src, fd.Name())
dstfp := path.Join(dst, fd.Name())
if fd.IsDir() {
if err = copyDir(srcfp, dstfp, replaces, ignores); err != nil {
return err
}
} else {
if err = copyFile(srcfp, dstfp, replaces); err != nil {
return err
}
}
}
return nil
}
func hasSets(name string, sets []string) bool {
for _, ig := range sets {
if ig == name {
return true
}
}
return false
}

@ -0,0 +1,71 @@
package base
import (
"context"
"errors"
"os"
"path"
"strings"
"github.com/go-git/go-git/v5"
)
// Repo is git repository manager.
type Repo struct {
url string
home string
}
// NewRepo new a repository manager.
func NewRepo(url string) *Repo {
return &Repo{
url: url,
home: kratosHomeWithDir("repo"),
}
}
func (r *Repo) Path() string {
start := strings.LastIndex(r.url, "/")
end := strings.LastIndex(r.url, ".git")
return path.Join(r.home, r.url[start+1:end])
}
func (r *Repo) Pull(ctx context.Context, url string) error {
repo, err := git.PlainOpen(r.Path())
if err != nil {
return err
}
w, err := repo.Worktree()
if err != nil {
return err
}
if err = w.PullContext(ctx, &git.PullOptions{
RemoteName: "origin",
Progress: os.Stdout,
}); errors.Is(err, git.NoErrAlreadyUpToDate) {
return nil
}
return err
}
func (r *Repo) Clone(ctx context.Context) error {
if _, err := os.Stat(r.Path()); !os.IsNotExist(err) {
return r.Pull(ctx, r.url)
}
_, err := git.PlainCloneContext(ctx, r.Path(), false, &git.CloneOptions{
URL: r.url,
Progress: os.Stdout,
})
return err
}
func (r *Repo) CopyTo(ctx context.Context, to string, modPath string, ignores []string) error {
if err := r.Clone(ctx); err != nil {
return err
}
mod, err := ModulePath(path.Join(r.Path(), "go.mod"))
if err != nil {
return err
}
return copyDir(r.Path(), to, []string{mod, modPath}, ignores)
}

@ -0,0 +1,16 @@
package base
import (
"context"
"testing"
)
func TestRepo(t *testing.T) {
r := NewRepo(RepoURL("https://github.com/go-kratos/service-layout.git"))
if err := r.Clone(context.Background()); err != nil {
t.Fatal(err)
}
if err := r.CopyTo(context.Background(), "/tmp/test_repo"); err != nil {
t.Fatal(err)
}
}

@ -0,0 +1,36 @@
package new
import (
"context"
"fmt"
"os"
"time"
"github.com/spf13/cobra"
)
// CmdNew represents the new command.
var CmdNew = &cobra.Command{
Use: "new",
Short: "Create a service template",
Long: "Create a service project using the repository template. Example: kratos new helloworld",
Run: run,
}
func run(cmd *cobra.Command, args []string) {
wd, err := os.Getwd()
if err != nil {
panic(err)
}
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
defer cancel()
if len(args) == 0 {
fmt.Fprintf(os.Stderr, "\033[31mERROR: project name is required.\033[m Example: kratos new helloworld\n")
return
}
p := &Project{Name: args[0]}
if err := p.Generate(ctx, wd); err != nil {
fmt.Fprintf(os.Stderr, "\033[31mERROR: %s\033[m\n", err)
return
}
}

@ -0,0 +1,38 @@
package new
import (
"context"
"fmt"
"os"
"path"
"github.com/go-kratos/kratos/cmd/kratos/internal/base"
)
const (
serviceLayoutURL = "https://github.com/go-kratos/kratos-layout.git"
)
// Project is a project template.
type Project struct {
Name string
}
// Generate generate template project.
func (p *Project) Generate(ctx context.Context, dir string) error {
to := path.Join(dir, p.Name)
if _, err := os.Stat(to); !os.IsNotExist(err) {
return fmt.Errorf("%s already exists", p.Name)
}
fmt.Printf("Creating service %s\n", p.Name)
repo := base.NewRepo(serviceLayoutURL)
if err := repo.CopyTo(ctx, to, p.Name, []string{".git", ".github"}); err != nil {
return err
}
os.Rename(
path.Join(to, "cmd", "server"),
path.Join(to, "cmd", p.Name),
)
return nil
}

@ -0,0 +1,65 @@
package add
import (
"fmt"
"io/ioutil"
"strings"
"github.com/spf13/cobra"
"golang.org/x/mod/modfile"
)
// CmdAdd represents the add command.
var CmdAdd = &cobra.Command{
Use: "add",
Short: "Add a proto API template",
Long: "Add a proto API template. Example: kratos add helloworld/v1/hello.proto",
Run: run,
}
func run(cmd *cobra.Command, args []string) {
// kratos add helloworld/v1/helloworld.proto
input := args[0]
n := strings.LastIndex(input, "/")
path := input[:n]
fileName := input[n+1:]
pkgName := strings.ReplaceAll(path, "/", ".")
p := &Proto{
Name: fileName,
Path: path,
Package: pkgName,
GoPackage: goPackage(path),
JavaPackage: javaPackage(pkgName),
Service: serviceName(fileName),
}
if err := p.Generate(); err != nil {
fmt.Println(err)
return
}
}
func modName() string {
modBytes, err := ioutil.ReadFile("go.mod")
if err != nil {
if modBytes, err = ioutil.ReadFile("../go.mod"); err != nil {
return ""
}
}
return modfile.ModulePath(modBytes)
}
func goPackage(path string) string {
s := strings.Split(path, "/")
return modName() + "/" + path + ";" + s[len(s)-1]
}
func javaPackage(name string) string {
return name
}
func serviceName(name string) string {
return unexport(strings.Split(name, ".")[0])
}
func unexport(s string) string { return strings.ToUpper(s[:1]) + s[1:] }

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

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

@ -0,0 +1,27 @@
package proto
import (
"github.com/go-kratos/kratos/cmd/kratos/internal/proto/add"
"github.com/go-kratos/kratos/cmd/kratos/internal/proto/service"
"github.com/go-kratos/kratos/cmd/kratos/internal/proto/source"
"github.com/spf13/cobra"
)
// CmdProto represents the proto command.
var CmdProto = &cobra.Command{
Use: "proto",
Short: "Generate the proto files",
Long: "Generate the proto files.",
Run: run,
}
func init() {
CmdProto.AddCommand(add.CmdAdd)
CmdProto.AddCommand(source.CmdSource)
CmdProto.AddCommand(service.CmdService)
}
func run(cmd *cobra.Command, args []string) {
}

@ -0,0 +1,86 @@
package service
import (
"fmt"
"io/ioutil"
"log"
"os"
"path"
"strings"
"github.com/emicklei/proto"
"github.com/spf13/cobra"
)
// CmdService represents the service command.
var CmdService = &cobra.Command{
Use: "service",
Short: "Generate the proto Service implementations",
Long: "Generate the proto Service implementations. Example: kratos proto service api/xxx.proto -target-dir=internal/service",
Run: run,
}
var targetDir string
func init() {
CmdService.Flags().StringVarP(&targetDir, "-target-dir", "t", "internal/service", "generate target directory")
}
func run(cmd *cobra.Command, args []string) {
if len(args) == 0 {
fmt.Fprintln(os.Stderr, "Please specify the proto file. Example: kratos proto service api/xxx.proto")
return
}
reader, err := os.Open(args[0])
if err != nil {
log.Fatal(err)
}
defer reader.Close()
parser := proto.NewParser(reader)
definition, err := parser.Parse()
if err != nil {
log.Fatal(err)
}
var (
pkg string
res []*Service
)
proto.Walk(definition,
proto.WithOption(func(o *proto.Option) {
if o.Name == "go_package" {
pkg = strings.Split(o.Constant.Source, ";")[0]
}
}),
proto.WithService(func(s *proto.Service) {
cs := &Service{
Package: pkg,
Service: s.Name,
}
for _, e := range s.Elements {
r, ok := e.(*proto.RPC)
if ok {
cs.Methods = append(cs.Methods, &Method{Service: s.Name, Name: r.Name, Request: r.RequestType, Reply: r.ReturnsType})
}
}
res = append(res, cs)
}),
)
for _, s := range res {
to := path.Join(targetDir, strings.ToLower(s.Service)+".go")
_, err := os.Stat(to)
if !os.IsNotExist(err) {
fmt.Fprintf(os.Stderr, "%s already exists\n", s.Service)
continue
}
if err = os.MkdirAll(targetDir, os.ModeDir); err != nil {
fmt.Fprintf(os.Stderr, "Failed to create file directory: %s\n", targetDir)
continue
}
b, err := s.execute()
if err != nil {
log.Fatal(err)
}
ioutil.WriteFile(to, b, 0644)
}
}

@ -0,0 +1,56 @@
package service
import (
"bytes"
"html/template"
)
var serviceTemplate = `
package service
import(
"context"
pb "{{.Package}}"
)
type {{.Service}}Service struct {
pb.Unimplemented{{.Service}}Server
}
func New{{.Service}}Service() pb.{{.Service}}Server {
return &{{.Service}}Service{}
}
{{ range .Methods }}
func (s *{{.Service}}Service) {{.Name}}(ctx context.Context, req *pb.{{.Request}}) (*pb.{{.Reply}}, error) {
return &pb.{{.Reply}}{}, nil
}
{{- end }}
`
// Service is a proto service.
type Service struct {
Package string
Service string
Methods []*Method
}
// Method is a proto method.
type Method struct {
Service string
Name string
Request string
Reply string
}
func (s *Service) execute() ([]byte, error) {
buf := new(bytes.Buffer)
tmpl, err := template.New("service").Parse(serviceTemplate)
if err != nil {
return nil, err
}
if err := tmpl.Execute(buf, s); err != nil {
return nil, err
}
return buf.Bytes(), nil
}

@ -0,0 +1,28 @@
package source
import (
"fmt"
"log"
"os/exec"
"github.com/spf13/cobra"
)
// CmdSource represents the source command.
var CmdSource = &cobra.Command{
Use: "source",
Short: "Generate the proto source code",
Long: "Generate the proto source code. Example: kratos proto source ./**/*.proto",
Run: run,
}
func run(cmd *cobra.Command, args []string) {
input := []string{"--go_out=paths=source_relative:.", "--go-grpc_out=paths=source_relative:."}
input = append(input, args...)
do := exec.Command("protoc", input...)
out, err := do.CombinedOutput()
if err != nil {
log.Fatalf("failed to execute: %s\n", err)
}
fmt.Println(string(out))
}

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

@ -0,0 +1,13 @@
package main
// go build -ldflags "-X main.Version=x.y.yz"
var (
// Version is the version of the compiled software.
Version string = "v2.0.0"
// Branch is current branch name the code is built off
Branch string
// Revision is the short commit hash of source tree
Revision string
// BuildDate is the date when the binary was built.
BuildDate string
)

@ -0,0 +1,58 @@
package main
import (
pb "github.com/go-kratos/kratos/v2/api/kratos/api"
"google.golang.org/protobuf/compiler/protogen"
"google.golang.org/protobuf/proto"
)
const (
errorsPackage = protogen.GoImportPath("github.com/go-kratos/kratos/v2/errors")
)
// generateFile generates a _http.pb.go file containing kratos errors definitions.
func generateFile(gen *protogen.Plugin, file *protogen.File) *protogen.GeneratedFile {
if len(file.Enums) == 0 {
return nil
}
filename := file.GeneratedFilenamePrefix + "_errors.pb.go"
g := gen.NewGeneratedFile(filename, file.GoImportPath)
g.P("// Code generated by protoc-gen-go-errors. DO NOT EDIT.")
g.P()
g.P("package ", file.GoPackageName)
g.P()
generateFileContent(gen, file, g)
return g
}
// generateFileContent generates the kratos errors definitions, excluding the package statement.
func generateFileContent(gen *protogen.Plugin, file *protogen.File, g *protogen.GeneratedFile) {
if len(file.Enums) == 0 {
return
}
g.P("// This is a compile-time assertion to ensure that this generated file")
g.P("// is compatible with the kratos package it is being compiled against.")
g.P("const _ = ", errorsPackage.Ident("SupportPackageIsVersion1"))
g.P()
for _, enum := range file.Enums {
genErrorsReason(gen, file, g, enum)
}
}
func genErrorsReason(gen *protogen.Plugin, file *protogen.File, g *protogen.GeneratedFile, enum *protogen.Enum) {
err := proto.GetExtension(enum.Desc.Options(), pb.E_Errors)
if ok := err.(bool); !ok {
return
}
var ew errorWrapper
for _, v := range enum.Values {
err := &errorInfo{
Name: string(enum.Desc.Name()),
Value: string(v.Desc.Name()),
}
ew.Errors = append(ew.Errors, err)
}
g.P(ew.execute())
}

@ -0,0 +1,9 @@
module github.com/go-kratos/kratos/cmd/protoc-gen-go-errors
go 1.15
require (
github.com/go-kratos/kratos/v2 v2.0.0-20210217083752-d86d233d93ce
github.com/golang/protobuf v1.4.3
google.golang.org/protobuf v1.25.0
)

@ -0,0 +1,101 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/go-kratos/kratos v0.6.0 h1:aGuIQQoj1EiWtBCIaPHvhPBcDx3WfL/Mw6q+5C5ehgg=
github.com/go-kratos/kratos/v2 v2.0.0-20201205165131-10618a745c96 h1:Qyiy167FmsHZucnc4vZ+hv938YyNTkodXVgb5PC2Y+U=
github.com/go-kratos/kratos/v2 v2.0.0-20201205165131-10618a745c96/go.mod h1:YwgE84UUomqngCzthva1VOYcuIZqXtYo5nTu22i1nbo=
github.com/go-kratos/kratos/v2 v2.0.0-20201205170920-f2b7f99a678c h1:V4dxjxZX4QEp8vAtumiA/6ibbqU9XZUUTA81piwk8nc=
github.com/go-kratos/kratos/v2 v2.0.0-20201205170920-f2b7f99a678c/go.mod h1:YwgE84UUomqngCzthva1VOYcuIZqXtYo5nTu22i1nbo=
github.com/go-kratos/kratos/v2 v2.0.0-20210207074933-00633b4860e1 h1:/blwKtlboosqLB9p+oICb8LkS13Ph0aP8V4rTil8pxE=
github.com/go-kratos/kratos/v2 v2.0.0-20210214123813-9afce08b3687 h1:3CVMCLzegnkCewvFoPBKqaYgmXG2zPCeV7Nro/JVyTk=
github.com/go-kratos/kratos/v2 v2.0.0-20210214172044-a42fa7820493 h1:39X3q7mGWL9yYZl9jwbHjLHzlsZlYajKQdewdignTVU=
github.com/go-kratos/kratos/v2 v2.0.0-20210214172044-a42fa7820493/go.mod h1:MIIjRu+ZXyn7IYlM1TBHbr75RLZOZM/CBBjT6luWL+Q=
github.com/go-kratos/kratos/v2 v2.0.0-20210217083752-d86d233d93ce h1:LfOsLN9s8tAxR8xIZGWQvEVWxHfipTnBSE0dvG4h3k8=
github.com/go-kratos/kratos/v2 v2.0.0-20210217083752-d86d233d93ce/go.mod h1:oLvFyDBJkkWN8TPqb+NmpvRrSy9uM/K+XQubVRc11a8=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.1 h1:ZFgWrT+bLgsYPirOnRfKLYJLvssAegOj/hgyMFdJZe0=
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM=
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
google.golang.org/genproto v0.0.0-20210114201628-6edceaf6022f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c=
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=

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

@ -0,0 +1,40 @@
package main
import (
"bytes"
"text/template"
)
var errorsTemplate = `const (
{{ range .Errors }}
Errors_{{.Value}} = "{{.Name}}_{{.Value}}"
{{- end }}
)
{{ range .Errors }}
func Is{{.Value}}(err error) bool {
return errors.Reason(err) == Errors_{{.Value}}
}
{{- end }}
`
type errorInfo struct {
Name string
Value string
}
type errorWrapper struct {
Errors []*errorInfo
}
func (e *errorWrapper) execute() string {
buf := new(bytes.Buffer)
tmpl, err := template.New("errors").Parse(errorsTemplate)
if err != nil {
panic(err)
}
if err := tmpl.Execute(buf, e); err != nil {
panic(err)
}
return string(buf.Bytes())
}

@ -0,0 +1,11 @@
module github.com/go-kratos/kratos/cmd/protoc-gen-go-http
go 1.15
require (
github.com/go-kratos/kratos/v2 v2.0.0-20210217083752-d86d233d93ce
github.com/golang/protobuf v1.4.3
google.golang.org/genproto v0.0.0-20210202153253-cf70463f6119
google.golang.org/grpc v1.35.0
google.golang.org/protobuf v1.25.0
)

@ -0,0 +1,99 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/go-kratos/kratos/v2 v2.0.0-20210210104528-47c8db1163db h1:p5pejan0Lbn7e43dQCUTzqOswWJqLIE5lMU5TUbwCt4=
github.com/go-kratos/kratos/v2 v2.0.0-20210210104528-47c8db1163db/go.mod h1:MIIjRu+ZXyn7IYlM1TBHbr75RLZOZM/CBBjT6luWL+Q=
github.com/go-kratos/kratos/v2 v2.0.0-20210211132943-131c3975fc3d/go.mod h1:MIIjRu+ZXyn7IYlM1TBHbr75RLZOZM/CBBjT6luWL+Q=
github.com/go-kratos/kratos/v2 v2.0.0-20210217083752-d86d233d93ce/go.mod h1:oLvFyDBJkkWN8TPqb+NmpvRrSy9uM/K+XQubVRc11a8=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM=
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.0 h1:/QaMHBdZ26BB3SSst0Iwl10Epc+xhTquomWX0oZEB6w=
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9 h1:L2auWcuQIvxz9xSEqzESnV/QN/gNRXNApHi3fYwl2w0=
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
google.golang.org/genproto v0.0.0-20210114201628-6edceaf6022f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210202153253-cf70463f6119 h1:m9+RjTMas6brUP8DBxSAa/WIPFy7FIhKpvk+9Ppce8E=
google.golang.org/genproto v0.0.0-20210202153253-cf70463f6119/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.35.0 h1:TwIQcH3es+MojMVojxxfQ3l3OF2KzlRxML2xZq0kRo8=
google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c=
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=

@ -0,0 +1,210 @@
package main
import (
"fmt"
"strings"
"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")
httpPackage = protogen.GoImportPath("net/http")
transportPackage = protogen.GoImportPath("github.com/go-kratos/kratos/v2/transport/http")
middlewarePackage = protogen.GoImportPath("github.com/go-kratos/kratos/v2/middleware")
)
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) *protogen.GeneratedFile {
if len(file.Services) == 0 {
return nil
}
filename := file.GeneratedFilenamePrefix + "_http.pb.go"
g := gen.NewGeneratedFile(filename, file.GoImportPath)
g.P("// Code generated by protoc-gen-go-http. DO NOT EDIT.")
g.P()
g.P("package ", file.GoPackageName)
g.P()
generateFileContent(gen, file, g)
return g
}
// generateFileContent generates the kratos errors definitions, excluding the package statement.
func generateFileContent(gen *protogen.Plugin, file *protogen.File, g *protogen.GeneratedFile) {
if len(file.Services) == 0 {
return
}
g.P("// This is a compile-time assertion to ensure that this generated file")
g.P("// is compatible with the kratos package it is being compiled against.")
g.P("// ", contextPackage.Ident(""), "/", httpPackage.Ident(""), "/", middlewarePackage.Ident(""))
g.P("const _ = ", transportPackage.Ident("SupportPackageIsVersion1"))
g.P()
for _, service := range file.Services {
genService(gen, file, g, service)
}
}
func genService(gen *protogen.Plugin, file *protogen.File, g *protogen.GeneratedFile, service *protogen.Service) {
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 {
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(method, bind))
}
sd.Methods = append(sd.Methods, buildHTTPRule(method, rule))
} else {
path := fmt.Sprintf("/%s/%s", service.Desc.FullName(), method.Desc.Name())
sd.Methods = append(sd.Methods, buildMethodDesc(method, "POST", path))
}
}
g.P(sd.execute())
}
func buildHTTPRule(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 = "GET"
case *annotations.HttpRule_Put:
path = pattern.Put
method = "PUT"
case *annotations.HttpRule_Post:
path = pattern.Post
method = "POST"
case *annotations.HttpRule_Delete:
path = pattern.Delete
method = "DELETE"
case *annotations.HttpRule_Patch:
path = pattern.Patch
method = "PATCH"
case *annotations.HttpRule_Custom:
path = pattern.Custom.Path
method = pattern.Custom.Kind
}
body = rule.Body
responseBody = rule.ResponseBody
md := buildMethodDesc(m, method, path)
if body != "" {
md.Body = "." + camelCaseVars(body)
}
if responseBody != "" {
md.ResponseBody = "." + camelCaseVars(responseBody)
}
return md
}
func buildMethodDesc(m *protogen.Method, method, path string) *methodDesc {
defer func() { methodSets[m.GoName]++ }()
return &methodDesc{
Name: m.GoName,
Num: methodSets[m.GoName],
Request: m.Input.GoIdent.GoName,
Reply: m.Output.GoIdent.GoName,
Path: path,
Method: method,
Vars: buildPathVars(m, path),
}
}
func buildPathVars(method *protogen.Method, path string) (res []string) {
for _, v := range strings.Split(path, "/") {
if strings.HasPrefix(v, "{") && strings.HasSuffix(v, "}") {
name := strings.TrimRight(strings.TrimLeft(v, "{"), "}")
res = append(res, name)
}
}
return
}
func camelCaseVars(s string) string {
var (
vars []string
subs = strings.Split(s, ".")
)
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 lowercases names, it's extremely unlikely to have two fields
// with different capitalizations.
// 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'
}
const deprecationComment = "// Deprecated: Do not use."

@ -0,0 +1,669 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.25.0
// protoc v3.13.0
// source: echo_service.proto
package testproto
import (
proto "github.com/golang/protobuf/proto"
_struct "github.com/golang/protobuf/ptypes/struct"
_ "google.golang.org/genproto/googleapis/api/annotations"
field_mask "google.golang.org/genproto/protobuf/field_mask"
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
// This is a compile-time assertion that a sufficiently up-to-date version
// of the legacy proto package is being used.
const _ = proto.ProtoPackageIsVersion4
type Corpus int32
const (
Corpus_UNIVERSAL Corpus = 0
Corpus_WEB Corpus = 1
Corpus_IMAGES Corpus = 2
Corpus_LOCAL Corpus = 3
Corpus_NEWS Corpus = 4
Corpus_PRODUCTS Corpus = 5
Corpus_VIDEO Corpus = 6
)
// Enum value maps for Corpus.
var (
Corpus_name = map[int32]string{
0: "UNIVERSAL",
1: "WEB",
2: "IMAGES",
3: "LOCAL",
4: "NEWS",
5: "PRODUCTS",
6: "VIDEO",
}
Corpus_value = map[string]int32{
"UNIVERSAL": 0,
"WEB": 1,
"IMAGES": 2,
"LOCAL": 3,
"NEWS": 4,
"PRODUCTS": 5,
"VIDEO": 6,
}
)
func (x Corpus) Enum() *Corpus {
p := new(Corpus)
*p = x
return p
}
func (x Corpus) String() string {
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
}
func (Corpus) Descriptor() protoreflect.EnumDescriptor {
return file_echo_service_proto_enumTypes[0].Descriptor()
}
func (Corpus) Type() protoreflect.EnumType {
return &file_echo_service_proto_enumTypes[0]
}
func (x Corpus) Number() protoreflect.EnumNumber {
return protoreflect.EnumNumber(x)
}
// Deprecated: Use Corpus.Descriptor instead.
func (Corpus) EnumDescriptor() ([]byte, []int) {
return file_echo_service_proto_rawDescGZIP(), []int{0}
}
// Embedded represents a message embedded in SimpleMessage.
type Embedded struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
// Types that are assignable to Mark:
// *Embedded_Progress
// *Embedded_Note
Mark isEmbedded_Mark `protobuf_oneof:"mark"`
}
func (x *Embedded) Reset() {
*x = Embedded{}
if protoimpl.UnsafeEnabled {
mi := &file_echo_service_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *Embedded) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*Embedded) ProtoMessage() {}
func (x *Embedded) ProtoReflect() protoreflect.Message {
mi := &file_echo_service_proto_msgTypes[0]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use Embedded.ProtoReflect.Descriptor instead.
func (*Embedded) Descriptor() ([]byte, []int) {
return file_echo_service_proto_rawDescGZIP(), []int{0}
}
func (m *Embedded) GetMark() isEmbedded_Mark {
if m != nil {
return m.Mark
}
return nil
}
func (x *Embedded) GetProgress() int64 {
if x, ok := x.GetMark().(*Embedded_Progress); ok {
return x.Progress
}
return 0
}
func (x *Embedded) GetNote() string {
if x, ok := x.GetMark().(*Embedded_Note); ok {
return x.Note
}
return ""
}
type isEmbedded_Mark interface {
isEmbedded_Mark()
}
type Embedded_Progress struct {
Progress int64 `protobuf:"varint,1,opt,name=progress,proto3,oneof"`
}
type Embedded_Note struct {
Note string `protobuf:"bytes,2,opt,name=note,proto3,oneof"`
}
func (*Embedded_Progress) isEmbedded_Mark() {}
func (*Embedded_Note) isEmbedded_Mark() {}
// SimpleMessage represents a simple message sent to the Echo service.
type SimpleMessage struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
// Id represents the message identifier.
Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
Num int64 `protobuf:"varint,2,opt,name=num,proto3" json:"num,omitempty"`
// Types that are assignable to Code:
// *SimpleMessage_LineNum
// *SimpleMessage_Lang
Code isSimpleMessage_Code `protobuf_oneof:"code"`
Status *Embedded `protobuf:"bytes,5,opt,name=status,proto3" json:"status,omitempty"`
// Types that are assignable to Ext:
// *SimpleMessage_En
// *SimpleMessage_No
Ext isSimpleMessage_Ext `protobuf_oneof:"ext"`
Corpus Corpus `protobuf:"varint,8,opt,name=corpus,proto3,enum=testproto.Corpus" json:"corpus,omitempty"`
}
func (x *SimpleMessage) Reset() {
*x = SimpleMessage{}
if protoimpl.UnsafeEnabled {
mi := &file_echo_service_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *SimpleMessage) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*SimpleMessage) ProtoMessage() {}
func (x *SimpleMessage) ProtoReflect() protoreflect.Message {
mi := &file_echo_service_proto_msgTypes[1]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use SimpleMessage.ProtoReflect.Descriptor instead.
func (*SimpleMessage) Descriptor() ([]byte, []int) {
return file_echo_service_proto_rawDescGZIP(), []int{1}
}
func (x *SimpleMessage) GetId() string {
if x != nil {
return x.Id
}
return ""
}
func (x *SimpleMessage) GetNum() int64 {
if x != nil {
return x.Num
}
return 0
}
func (m *SimpleMessage) GetCode() isSimpleMessage_Code {
if m != nil {
return m.Code
}
return nil
}
func (x *SimpleMessage) GetLineNum() int64 {
if x, ok := x.GetCode().(*SimpleMessage_LineNum); ok {
return x.LineNum
}
return 0
}
func (x *SimpleMessage) GetLang() string {
if x, ok := x.GetCode().(*SimpleMessage_Lang); ok {
return x.Lang
}
return ""
}
func (x *SimpleMessage) GetStatus() *Embedded {
if x != nil {
return x.Status
}
return nil
}
func (m *SimpleMessage) GetExt() isSimpleMessage_Ext {
if m != nil {
return m.Ext
}
return nil
}
func (x *SimpleMessage) GetEn() int64 {
if x, ok := x.GetExt().(*SimpleMessage_En); ok {
return x.En
}
return 0
}
func (x *SimpleMessage) GetNo() *Embedded {
if x, ok := x.GetExt().(*SimpleMessage_No); ok {
return x.No
}
return nil
}
func (x *SimpleMessage) GetCorpus() Corpus {
if x != nil {
return x.Corpus
}
return Corpus_UNIVERSAL
}
type isSimpleMessage_Code interface {
isSimpleMessage_Code()
}
type SimpleMessage_LineNum struct {
LineNum int64 `protobuf:"varint,3,opt,name=line_num,json=lineNum,proto3,oneof"`
}
type SimpleMessage_Lang struct {
Lang string `protobuf:"bytes,4,opt,name=lang,proto3,oneof"`
}
func (*SimpleMessage_LineNum) isSimpleMessage_Code() {}
func (*SimpleMessage_Lang) isSimpleMessage_Code() {}
type isSimpleMessage_Ext interface {
isSimpleMessage_Ext()
}
type SimpleMessage_En struct {
En int64 `protobuf:"varint,6,opt,name=en,proto3,oneof"`
}
type SimpleMessage_No struct {
No *Embedded `protobuf:"bytes,7,opt,name=no,proto3,oneof"`
}
func (*SimpleMessage_En) isSimpleMessage_Ext() {}
func (*SimpleMessage_No) isSimpleMessage_Ext() {}
// DynamicMessage represents a message which can have its structure
// built dynamically using Struct and Values.
type DynamicMessage struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
StructField *_struct.Struct `protobuf:"bytes,1,opt,name=struct_field,json=structField,proto3" json:"struct_field,omitempty"`
ValueField *_struct.Value `protobuf:"bytes,2,opt,name=value_field,json=valueField,proto3" json:"value_field,omitempty"`
}
func (x *DynamicMessage) Reset() {
*x = DynamicMessage{}
if protoimpl.UnsafeEnabled {
mi := &file_echo_service_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *DynamicMessage) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*DynamicMessage) ProtoMessage() {}
func (x *DynamicMessage) ProtoReflect() protoreflect.Message {
mi := &file_echo_service_proto_msgTypes[2]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use DynamicMessage.ProtoReflect.Descriptor instead.
func (*DynamicMessage) Descriptor() ([]byte, []int) {
return file_echo_service_proto_rawDescGZIP(), []int{2}
}
func (x *DynamicMessage) GetStructField() *_struct.Struct {
if x != nil {
return x.StructField
}
return nil
}
func (x *DynamicMessage) GetValueField() *_struct.Value {
if x != nil {
return x.ValueField
}
return nil
}
type DynamicMessageUpdate struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Body *DynamicMessage `protobuf:"bytes,1,opt,name=body,proto3" json:"body,omitempty"`
UpdateMask *field_mask.FieldMask `protobuf:"bytes,2,opt,name=update_mask,json=updateMask,proto3" json:"update_mask,omitempty"`
}
func (x *DynamicMessageUpdate) Reset() {
*x = DynamicMessageUpdate{}
if protoimpl.UnsafeEnabled {
mi := &file_echo_service_proto_msgTypes[3]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *DynamicMessageUpdate) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*DynamicMessageUpdate) ProtoMessage() {}
func (x *DynamicMessageUpdate) ProtoReflect() protoreflect.Message {
mi := &file_echo_service_proto_msgTypes[3]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use DynamicMessageUpdate.ProtoReflect.Descriptor instead.
func (*DynamicMessageUpdate) Descriptor() ([]byte, []int) {
return file_echo_service_proto_rawDescGZIP(), []int{3}
}
func (x *DynamicMessageUpdate) GetBody() *DynamicMessage {
if x != nil {
return x.Body
}
return nil
}
func (x *DynamicMessageUpdate) GetUpdateMask() *field_mask.FieldMask {
if x != nil {
return x.UpdateMask
}
return nil
}
var File_echo_service_proto protoreflect.FileDescriptor
var file_echo_service_proto_rawDesc = []byte{
0x0a, 0x12, 0x65, 0x63, 0x68, 0x6f, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x70,
0x72, 0x6f, 0x74, 0x6f, 0x12, 0x09, 0x74, 0x65, 0x73, 0x74, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a,
0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x61, 0x6e, 0x6e, 0x6f,
0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x20, 0x67,
0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x66,
0x69, 0x65, 0x6c, 0x64, 0x5f, 0x6d, 0x61, 0x73, 0x6b, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a,
0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66,
0x2f, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x46, 0x0a,
0x08, 0x45, 0x6d, 0x62, 0x65, 0x64, 0x64, 0x65, 0x64, 0x12, 0x1c, 0x0a, 0x08, 0x70, 0x72, 0x6f,
0x67, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x48, 0x00, 0x52, 0x08, 0x70,
0x72, 0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, 0x12, 0x14, 0x0a, 0x04, 0x6e, 0x6f, 0x74, 0x65, 0x18,
0x02, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x04, 0x6e, 0x6f, 0x74, 0x65, 0x42, 0x06, 0x0a,
0x04, 0x6d, 0x61, 0x72, 0x6b, 0x22, 0x84, 0x02, 0x0a, 0x0d, 0x53, 0x69, 0x6d, 0x70, 0x6c, 0x65,
0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20,
0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x6e, 0x75, 0x6d, 0x18, 0x02,
0x20, 0x01, 0x28, 0x03, 0x52, 0x03, 0x6e, 0x75, 0x6d, 0x12, 0x1b, 0x0a, 0x08, 0x6c, 0x69, 0x6e,
0x65, 0x5f, 0x6e, 0x75, 0x6d, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x48, 0x00, 0x52, 0x07, 0x6c,
0x69, 0x6e, 0x65, 0x4e, 0x75, 0x6d, 0x12, 0x14, 0x0a, 0x04, 0x6c, 0x61, 0x6e, 0x67, 0x18, 0x04,
0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x04, 0x6c, 0x61, 0x6e, 0x67, 0x12, 0x2b, 0x0a, 0x06,
0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x74,
0x65, 0x73, 0x74, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x45, 0x6d, 0x62, 0x65, 0x64, 0x64, 0x65,
0x64, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x10, 0x0a, 0x02, 0x65, 0x6e, 0x18,
0x06, 0x20, 0x01, 0x28, 0x03, 0x48, 0x01, 0x52, 0x02, 0x65, 0x6e, 0x12, 0x25, 0x0a, 0x02, 0x6e,
0x6f, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x70, 0x72,
0x6f, 0x74, 0x6f, 0x2e, 0x45, 0x6d, 0x62, 0x65, 0x64, 0x64, 0x65, 0x64, 0x48, 0x01, 0x52, 0x02,
0x6e, 0x6f, 0x12, 0x29, 0x0a, 0x06, 0x63, 0x6f, 0x72, 0x70, 0x75, 0x73, 0x18, 0x08, 0x20, 0x01,
0x28, 0x0e, 0x32, 0x11, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x43,
0x6f, 0x72, 0x70, 0x75, 0x73, 0x52, 0x06, 0x63, 0x6f, 0x72, 0x70, 0x75, 0x73, 0x42, 0x06, 0x0a,
0x04, 0x63, 0x6f, 0x64, 0x65, 0x42, 0x05, 0x0a, 0x03, 0x65, 0x78, 0x74, 0x22, 0x85, 0x01, 0x0a,
0x0e, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12,
0x3a, 0x0a, 0x0c, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x5f, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x18,
0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70,
0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x0b,
0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x12, 0x37, 0x0a, 0x0b, 0x76,
0x61, 0x6c, 0x75, 0x65, 0x5f, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b,
0x32, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62,
0x75, 0x66, 0x2e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0a, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x46,
0x69, 0x65, 0x6c, 0x64, 0x22, 0x82, 0x01, 0x0a, 0x14, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63,
0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x2d, 0x0a,
0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x74, 0x65,
0x73, 0x74, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x4d,
0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x12, 0x3b, 0x0a, 0x0b,
0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x6d, 0x61, 0x73, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28,
0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
0x62, 0x75, 0x66, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x4d, 0x61, 0x73, 0x6b, 0x52, 0x0a, 0x75,
0x70, 0x64, 0x61, 0x74, 0x65, 0x4d, 0x61, 0x73, 0x6b, 0x2a, 0x5a, 0x0a, 0x06, 0x43, 0x6f, 0x72,
0x70, 0x75, 0x73, 0x12, 0x0d, 0x0a, 0x09, 0x55, 0x4e, 0x49, 0x56, 0x45, 0x52, 0x53, 0x41, 0x4c,
0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x57, 0x45, 0x42, 0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06, 0x49,
0x4d, 0x41, 0x47, 0x45, 0x53, 0x10, 0x02, 0x12, 0x09, 0x0a, 0x05, 0x4c, 0x4f, 0x43, 0x41, 0x4c,
0x10, 0x03, 0x12, 0x08, 0x0a, 0x04, 0x4e, 0x45, 0x57, 0x53, 0x10, 0x04, 0x12, 0x0c, 0x0a, 0x08,
0x50, 0x52, 0x4f, 0x44, 0x55, 0x43, 0x54, 0x53, 0x10, 0x05, 0x12, 0x09, 0x0a, 0x05, 0x56, 0x49,
0x44, 0x45, 0x4f, 0x10, 0x06, 0x32, 0xbc, 0x04, 0x0a, 0x0b, 0x45, 0x63, 0x68, 0x6f, 0x53, 0x65,
0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0xf2, 0x01, 0x0a, 0x04, 0x45, 0x63, 0x68, 0x6f, 0x12, 0x18,
0x2e, 0x74, 0x65, 0x73, 0x74, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x69, 0x6d, 0x70, 0x6c,
0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x18, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x70,
0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61,
0x67, 0x65, 0x22, 0xb5, 0x01, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0xae, 0x01, 0x22, 0x15, 0x2f, 0x76,
0x31, 0x2f, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2f, 0x65, 0x63, 0x68, 0x6f, 0x2f, 0x7b,
0x69, 0x64, 0x7d, 0x5a, 0x1d, 0x12, 0x1b, 0x2f, 0x76, 0x31, 0x2f, 0x65, 0x78, 0x61, 0x6d, 0x70,
0x6c, 0x65, 0x2f, 0x65, 0x63, 0x68, 0x6f, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x2f, 0x7b, 0x6e, 0x75,
0x6d, 0x7d, 0x5a, 0x24, 0x12, 0x22, 0x2f, 0x76, 0x31, 0x2f, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c,
0x65, 0x2f, 0x65, 0x63, 0x68, 0x6f, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x2f, 0x7b, 0x6e, 0x75, 0x6d,
0x7d, 0x2f, 0x7b, 0x6c, 0x61, 0x6e, 0x67, 0x7d, 0x5a, 0x31, 0x12, 0x2f, 0x2f, 0x76, 0x31, 0x2f,
0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2f, 0x65, 0x63, 0x68, 0x6f, 0x31, 0x2f, 0x7b, 0x69,
0x64, 0x7d, 0x2f, 0x7b, 0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x6e, 0x75, 0x6d, 0x7d, 0x2f, 0x7b, 0x73,
0x74, 0x61, 0x74, 0x75, 0x73, 0x2e, 0x6e, 0x6f, 0x74, 0x65, 0x7d, 0x5a, 0x1d, 0x12, 0x1b, 0x2f,
0x76, 0x31, 0x2f, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2f, 0x65, 0x63, 0x68, 0x6f, 0x32,
0x2f, 0x7b, 0x6e, 0x6f, 0x2e, 0x6e, 0x6f, 0x74, 0x65, 0x7d, 0x12, 0x60, 0x0a, 0x08, 0x45, 0x63,
0x68, 0x6f, 0x42, 0x6f, 0x64, 0x79, 0x12, 0x18, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x70, 0x72, 0x6f,
0x74, 0x6f, 0x2e, 0x53, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65,
0x1a, 0x18, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x69, 0x6d,
0x70, 0x6c, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x20, 0x82, 0xd3, 0xe4, 0x93,
0x02, 0x1a, 0x22, 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2f,
0x65, 0x63, 0x68, 0x6f, 0x5f, 0x62, 0x6f, 0x64, 0x79, 0x3a, 0x01, 0x2a, 0x12, 0x61, 0x0a, 0x0a,
0x45, 0x63, 0x68, 0x6f, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x18, 0x2e, 0x74, 0x65, 0x73,
0x74, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x4d, 0x65, 0x73,
0x73, 0x61, 0x67, 0x65, 0x1a, 0x18, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x70, 0x72, 0x6f, 0x74, 0x6f,
0x2e, 0x53, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x1f,
0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x2a, 0x17, 0x2f, 0x76, 0x31, 0x2f, 0x65, 0x78, 0x61, 0x6d,
0x70, 0x6c, 0x65, 0x2f, 0x65, 0x63, 0x68, 0x6f, 0x5f, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x12,
0x73, 0x0a, 0x09, 0x45, 0x63, 0x68, 0x6f, 0x50, 0x61, 0x74, 0x63, 0x68, 0x12, 0x1f, 0x2e, 0x74,
0x65, 0x73, 0x74, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63,
0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x1a, 0x1f, 0x2e,
0x74, 0x65, 0x73, 0x74, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69,
0x63, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x22, 0x24,
0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1e, 0x32, 0x16, 0x2f, 0x76, 0x31, 0x2f, 0x65, 0x78, 0x61, 0x6d,
0x70, 0x6c, 0x65, 0x2f, 0x65, 0x63, 0x68, 0x6f, 0x5f, 0x70, 0x61, 0x74, 0x63, 0x68, 0x3a, 0x04,
0x62, 0x6f, 0x64, 0x79, 0x42, 0x51, 0x5a, 0x4f, 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, 0x63, 0x6d, 0x64, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x2d, 0x67,
0x65, 0x6e, 0x2d, 0x67, 0x6f, 0x2d, 0x68, 0x74, 0x74, 0x70, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72,
0x6e, 0x61, 0x6c, 0x2f, 0x74, 0x65, 0x73, 0x74, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x3b, 0x74, 0x65,
0x73, 0x74, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
file_echo_service_proto_rawDescOnce sync.Once
file_echo_service_proto_rawDescData = file_echo_service_proto_rawDesc
)
func file_echo_service_proto_rawDescGZIP() []byte {
file_echo_service_proto_rawDescOnce.Do(func() {
file_echo_service_proto_rawDescData = protoimpl.X.CompressGZIP(file_echo_service_proto_rawDescData)
})
return file_echo_service_proto_rawDescData
}
var file_echo_service_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
var file_echo_service_proto_msgTypes = make([]protoimpl.MessageInfo, 4)
var file_echo_service_proto_goTypes = []interface{}{
(Corpus)(0), // 0: testproto.Corpus
(*Embedded)(nil), // 1: testproto.Embedded
(*SimpleMessage)(nil), // 2: testproto.SimpleMessage
(*DynamicMessage)(nil), // 3: testproto.DynamicMessage
(*DynamicMessageUpdate)(nil), // 4: testproto.DynamicMessageUpdate
(*_struct.Struct)(nil), // 5: google.protobuf.Struct
(*_struct.Value)(nil), // 6: google.protobuf.Value
(*field_mask.FieldMask)(nil), // 7: google.protobuf.FieldMask
}
var file_echo_service_proto_depIdxs = []int32{
1, // 0: testproto.SimpleMessage.status:type_name -> testproto.Embedded
1, // 1: testproto.SimpleMessage.no:type_name -> testproto.Embedded
0, // 2: testproto.SimpleMessage.corpus:type_name -> testproto.Corpus
5, // 3: testproto.DynamicMessage.struct_field:type_name -> google.protobuf.Struct
6, // 4: testproto.DynamicMessage.value_field:type_name -> google.protobuf.Value
3, // 5: testproto.DynamicMessageUpdate.body:type_name -> testproto.DynamicMessage
7, // 6: testproto.DynamicMessageUpdate.update_mask:type_name -> google.protobuf.FieldMask
2, // 7: testproto.EchoService.Echo:input_type -> testproto.SimpleMessage
2, // 8: testproto.EchoService.EchoBody:input_type -> testproto.SimpleMessage
2, // 9: testproto.EchoService.EchoDelete:input_type -> testproto.SimpleMessage
4, // 10: testproto.EchoService.EchoPatch:input_type -> testproto.DynamicMessageUpdate
2, // 11: testproto.EchoService.Echo:output_type -> testproto.SimpleMessage
2, // 12: testproto.EchoService.EchoBody:output_type -> testproto.SimpleMessage
2, // 13: testproto.EchoService.EchoDelete:output_type -> testproto.SimpleMessage
4, // 14: testproto.EchoService.EchoPatch:output_type -> testproto.DynamicMessageUpdate
11, // [11:15] is the sub-list for method output_type
7, // [7:11] is the sub-list for method input_type
7, // [7:7] is the sub-list for extension type_name
7, // [7:7] is the sub-list for extension extendee
0, // [0:7] is the sub-list for field type_name
}
func init() { file_echo_service_proto_init() }
func file_echo_service_proto_init() {
if File_echo_service_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
file_echo_service_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*Embedded); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_echo_service_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*SimpleMessage); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_echo_service_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*DynamicMessage); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_echo_service_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*DynamicMessageUpdate); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
file_echo_service_proto_msgTypes[0].OneofWrappers = []interface{}{
(*Embedded_Progress)(nil),
(*Embedded_Note)(nil),
}
file_echo_service_proto_msgTypes[1].OneofWrappers = []interface{}{
(*SimpleMessage_LineNum)(nil),
(*SimpleMessage_Lang)(nil),
(*SimpleMessage_En)(nil),
(*SimpleMessage_No)(nil),
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_echo_service_proto_rawDesc,
NumEnums: 1,
NumMessages: 4,
NumExtensions: 0,
NumServices: 1,
},
GoTypes: file_echo_service_proto_goTypes,
DependencyIndexes: file_echo_service_proto_depIdxs,
EnumInfos: file_echo_service_proto_enumTypes,
MessageInfos: file_echo_service_proto_msgTypes,
}.Build()
File_echo_service_proto = out.File
file_echo_service_proto_rawDesc = nil
file_echo_service_proto_goTypes = nil
file_echo_service_proto_depIdxs = nil
}

@ -0,0 +1,101 @@
syntax = "proto3";
option go_package = "github.com/go-kratos/kratos/cmd/protoc-gen-go-http/internal/testproto;testproto";
package testproto;
import "google/api/annotations.proto";
import "google/protobuf/field_mask.proto";
import "google/protobuf/struct.proto";
enum Corpus {
UNIVERSAL = 0;
WEB = 1;
IMAGES = 2;
LOCAL = 3;
NEWS = 4;
PRODUCTS = 5;
VIDEO = 6;
}
// Embedded represents a message embedded in SimpleMessage.
message Embedded {
oneof mark {
int64 progress = 1;
string note = 2;
}
}
// SimpleMessage represents a simple message sent to the Echo service.
message SimpleMessage {
// Id represents the message identifier.
string id = 1;
int64 num = 2;
oneof code {
int64 line_num = 3;
string lang = 4;
}
Embedded status = 5;
oneof ext {
int64 en = 6;
Embedded no = 7;
}
Corpus corpus = 8;
}
// DynamicMessage represents a message which can have its structure
// built dynamically using Struct and Values.
message DynamicMessage {
google.protobuf.Struct struct_field = 1;
google.protobuf.Value value_field = 2;
}
message DynamicMessageUpdate {
DynamicMessage body = 1;
google.protobuf.FieldMask update_mask = 2;
}
// Echo service responds to incoming echo requests.
service EchoService {
// Echo method receives a simple message and returns it.
//
// The message posted as the id parameter will also be
// returned.
rpc Echo(SimpleMessage) returns (SimpleMessage) {
option (google.api.http) = {
post: "/v1/example/echo/{id}"
additional_bindings {
get: "/v1/example/echo/{id}/{num}"
}
additional_bindings {
get: "/v1/example/echo/{id}/{num}/{lang}"
}
additional_bindings {
get: "/v1/example/echo1/{id}/{line_num}/{status.note}"
}
additional_bindings {
get: "/v1/example/echo2/{no.note}"
}
};
}
// EchoBody method receives a simple message and returns it.
rpc EchoBody(SimpleMessage) returns (SimpleMessage) {
option (google.api.http) = {
post: "/v1/example/echo_body"
body: "*"
};
}
// EchoDelete method receives a simple message and returns it.
rpc EchoDelete(SimpleMessage) returns (SimpleMessage) {
option (google.api.http) = {
delete: "/v1/example/echo_delete"
};
}
// EchoPatch method receives a NonStandardUpdateRequest and returns it.
rpc EchoPatch(DynamicMessageUpdate) returns (DynamicMessageUpdate) {
option (google.api.http) = {
patch: "/v1/example/echo_patch"
body: "body"
};
}
}

@ -0,0 +1,219 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
package testproto
import (
context "context"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"
)
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
const _ = grpc.SupportPackageIsVersion7
// EchoServiceClient is the client API for EchoService service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
type EchoServiceClient interface {
// Echo method receives a simple message and returns it.
//
// The message posted as the id parameter will also be
// returned.
Echo(ctx context.Context, in *SimpleMessage, opts ...grpc.CallOption) (*SimpleMessage, error)
// EchoBody method receives a simple message and returns it.
EchoBody(ctx context.Context, in *SimpleMessage, opts ...grpc.CallOption) (*SimpleMessage, error)
// EchoDelete method receives a simple message and returns it.
EchoDelete(ctx context.Context, in *SimpleMessage, opts ...grpc.CallOption) (*SimpleMessage, error)
// EchoPatch method receives a NonStandardUpdateRequest and returns it.
EchoPatch(ctx context.Context, in *DynamicMessageUpdate, opts ...grpc.CallOption) (*DynamicMessageUpdate, error)
}
type echoServiceClient struct {
cc grpc.ClientConnInterface
}
func NewEchoServiceClient(cc grpc.ClientConnInterface) EchoServiceClient {
return &echoServiceClient{cc}
}
func (c *echoServiceClient) Echo(ctx context.Context, in *SimpleMessage, opts ...grpc.CallOption) (*SimpleMessage, error) {
out := new(SimpleMessage)
err := c.cc.Invoke(ctx, "/testproto.EchoService/Echo", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *echoServiceClient) EchoBody(ctx context.Context, in *SimpleMessage, opts ...grpc.CallOption) (*SimpleMessage, error) {
out := new(SimpleMessage)
err := c.cc.Invoke(ctx, "/testproto.EchoService/EchoBody", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *echoServiceClient) EchoDelete(ctx context.Context, in *SimpleMessage, opts ...grpc.CallOption) (*SimpleMessage, error) {
out := new(SimpleMessage)
err := c.cc.Invoke(ctx, "/testproto.EchoService/EchoDelete", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *echoServiceClient) EchoPatch(ctx context.Context, in *DynamicMessageUpdate, opts ...grpc.CallOption) (*DynamicMessageUpdate, error) {
out := new(DynamicMessageUpdate)
err := c.cc.Invoke(ctx, "/testproto.EchoService/EchoPatch", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// EchoServiceServer is the server API for EchoService service.
// All implementations must embed UnimplementedEchoServiceServer
// for forward compatibility
type EchoServiceServer interface {
// Echo method receives a simple message and returns it.
//
// The message posted as the id parameter will also be
// returned.
Echo(context.Context, *SimpleMessage) (*SimpleMessage, error)
// EchoBody method receives a simple message and returns it.
EchoBody(context.Context, *SimpleMessage) (*SimpleMessage, error)
// EchoDelete method receives a simple message and returns it.
EchoDelete(context.Context, *SimpleMessage) (*SimpleMessage, error)
// EchoPatch method receives a NonStandardUpdateRequest and returns it.
EchoPatch(context.Context, *DynamicMessageUpdate) (*DynamicMessageUpdate, error)
mustEmbedUnimplementedEchoServiceServer()
}
// UnimplementedEchoServiceServer must be embedded to have forward compatible implementations.
type UnimplementedEchoServiceServer struct {
}
func (UnimplementedEchoServiceServer) Echo(context.Context, *SimpleMessage) (*SimpleMessage, error) {
return nil, status.Errorf(codes.Unimplemented, "method Echo not implemented")
}
func (UnimplementedEchoServiceServer) EchoBody(context.Context, *SimpleMessage) (*SimpleMessage, error) {
return nil, status.Errorf(codes.Unimplemented, "method EchoBody not implemented")
}
func (UnimplementedEchoServiceServer) EchoDelete(context.Context, *SimpleMessage) (*SimpleMessage, error) {
return nil, status.Errorf(codes.Unimplemented, "method EchoDelete not implemented")
}
func (UnimplementedEchoServiceServer) EchoPatch(context.Context, *DynamicMessageUpdate) (*DynamicMessageUpdate, error) {
return nil, status.Errorf(codes.Unimplemented, "method EchoPatch not implemented")
}
func (UnimplementedEchoServiceServer) mustEmbedUnimplementedEchoServiceServer() {}
// UnsafeEchoServiceServer may be embedded to opt out of forward compatibility for this service.
// Use of this interface is not recommended, as added methods to EchoServiceServer will
// result in compilation errors.
type UnsafeEchoServiceServer interface {
mustEmbedUnimplementedEchoServiceServer()
}
func RegisterEchoServiceServer(s grpc.ServiceRegistrar, srv EchoServiceServer) {
s.RegisterService(&_EchoService_serviceDesc, srv)
}
func _EchoService_Echo_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(SimpleMessage)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(EchoServiceServer).Echo(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/testproto.EchoService/Echo",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(EchoServiceServer).Echo(ctx, req.(*SimpleMessage))
}
return interceptor(ctx, in, info, handler)
}
func _EchoService_EchoBody_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(SimpleMessage)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(EchoServiceServer).EchoBody(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/testproto.EchoService/EchoBody",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(EchoServiceServer).EchoBody(ctx, req.(*SimpleMessage))
}
return interceptor(ctx, in, info, handler)
}
func _EchoService_EchoDelete_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(SimpleMessage)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(EchoServiceServer).EchoDelete(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/testproto.EchoService/EchoDelete",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(EchoServiceServer).EchoDelete(ctx, req.(*SimpleMessage))
}
return interceptor(ctx, in, info, handler)
}
func _EchoService_EchoPatch_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(DynamicMessageUpdate)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(EchoServiceServer).EchoPatch(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/testproto.EchoService/EchoPatch",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(EchoServiceServer).EchoPatch(ctx, req.(*DynamicMessageUpdate))
}
return interceptor(ctx, in, info, handler)
}
var _EchoService_serviceDesc = grpc.ServiceDesc{
ServiceName: "testproto.EchoService",
HandlerType: (*EchoServiceServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "Echo",
Handler: _EchoService_Echo_Handler,
},
{
MethodName: "EchoBody",
Handler: _EchoService_EchoBody_Handler,
},
{
MethodName: "EchoDelete",
Handler: _EchoService_EchoDelete_Handler,
},
{
MethodName: "EchoPatch",
Handler: _EchoService_EchoPatch_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "echo_service.proto",
}

@ -0,0 +1,240 @@
// Code generated by protoc-gen-go-http. DO NOT EDIT.
package testproto
import (
context "context"
middleware "github.com/go-kratos/kratos/v2/middleware"
http1 "github.com/go-kratos/kratos/v2/transport/http"
http "net/http"
)
// This is a compile-time assertion to ensure that this generated file
// is compatible with the kratos package it is being compiled against.
// context./http./middleware.
const _ = http1.SupportPackageIsVersion1
type EchoServiceHTTPServer interface {
Echo(context.Context, *SimpleMessage) (*SimpleMessage, error)
EchoBody(context.Context, *SimpleMessage) (*SimpleMessage, error)
EchoDelete(context.Context, *SimpleMessage) (*SimpleMessage, error)
EchoPatch(context.Context, *DynamicMessageUpdate) (*DynamicMessageUpdate, error)
}
func RegisterEchoServiceHTTPServer(s http1.ServiceRegistrar, srv EchoServiceHTTPServer) {
s.RegisterService(&_HTTP_EchoService_serviceDesc, srv)
}
func _HTTP_EchoService_Echo_0(srv interface{}, ctx context.Context, req *http.Request, dec func(interface{}) error, m middleware.Middleware) (interface{}, error) {
var in SimpleMessage
if err := http1.BindVars(req, &in); err != nil {
return nil, err
}
if err := http1.BindForm(req, &in); err != nil {
return nil, err
}
h := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(EchoServiceServer).Echo(ctx, &in)
}
out, err := m(h)(ctx, &in)
if err != nil {
return nil, err
}
return out, nil
}
func _HTTP_EchoService_Echo_1(srv interface{}, ctx context.Context, req *http.Request, dec func(interface{}) error, m middleware.Middleware) (interface{}, error) {
var in SimpleMessage
if err := http1.BindVars(req, &in); err != nil {
return nil, err
}
if err := http1.BindForm(req, &in); err != nil {
return nil, err
}
h := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(EchoServiceServer).Echo(ctx, &in)
}
out, err := m(h)(ctx, &in)
if err != nil {
return nil, err
}
return out, nil
}
func _HTTP_EchoService_Echo_2(srv interface{}, ctx context.Context, req *http.Request, dec func(interface{}) error, m middleware.Middleware) (interface{}, error) {
var in SimpleMessage
if err := http1.BindVars(req, &in); err != nil {
return nil, err
}
if err := http1.BindForm(req, &in); err != nil {
return nil, err
}
h := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(EchoServiceServer).Echo(ctx, &in)
}
out, err := m(h)(ctx, &in)
if err != nil {
return nil, err
}
return out, nil
}
func _HTTP_EchoService_Echo_3(srv interface{}, ctx context.Context, req *http.Request, dec func(interface{}) error, m middleware.Middleware) (interface{}, error) {
var in SimpleMessage
if err := http1.BindVars(req, &in); err != nil {
return nil, err
}
if err := http1.BindForm(req, &in); err != nil {
return nil, err
}
h := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(EchoServiceServer).Echo(ctx, &in)
}
out, err := m(h)(ctx, &in)
if err != nil {
return nil, err
}
return out, nil
}
func _HTTP_EchoService_Echo_4(srv interface{}, ctx context.Context, req *http.Request, dec func(interface{}) error, m middleware.Middleware) (interface{}, error) {
var in SimpleMessage
if err := http1.BindVars(req, &in); err != nil {
return nil, err
}
if err := http1.BindForm(req, &in); err != nil {
return nil, err
}
h := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(EchoServiceServer).Echo(ctx, &in)
}
out, err := m(h)(ctx, &in)
if err != nil {
return nil, err
}
return out, nil
}
func _HTTP_EchoService_EchoBody_0(srv interface{}, ctx context.Context, req *http.Request, dec func(interface{}) error, m middleware.Middleware) (interface{}, error) {
var in SimpleMessage
if err := dec(&in); err != nil {
return nil, err
}
h := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(EchoServiceServer).EchoBody(ctx, &in)
}
out, err := m(h)(ctx, &in)
if err != nil {
return nil, err
}
return out, nil
}
func _HTTP_EchoService_EchoDelete_0(srv interface{}, ctx context.Context, req *http.Request, dec func(interface{}) error, m middleware.Middleware) (interface{}, error) {
var in SimpleMessage
if err := http1.BindForm(req, &in); err != nil {
return nil, err
}
h := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(EchoServiceServer).EchoDelete(ctx, &in)
}
out, err := m(h)(ctx, &in)
if err != nil {
return nil, err
}
return out, nil
}
func _HTTP_EchoService_EchoPatch_0(srv interface{}, ctx context.Context, req *http.Request, dec func(interface{}) error, m middleware.Middleware) (interface{}, error) {
var in DynamicMessageUpdate
if err := dec(in.Body); err != nil {
return nil, err
}
h := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(EchoServiceServer).EchoPatch(ctx, &in)
}
out, err := m(h)(ctx, &in)
if err != nil {
return nil, err
}
return out, nil
}
var _HTTP_EchoService_serviceDesc = http1.ServiceDesc{
ServiceName: "testproto.EchoService",
Methods: []http1.MethodDesc{
{
Path: "/v1/example/echo/{id}/{num}",
Method: "GET",
Handler: _HTTP_EchoService_Echo_0,
},
{
Path: "/v1/example/echo/{id}/{num}/{lang}",
Method: "GET",
Handler: _HTTP_EchoService_Echo_1,
},
{
Path: "/v1/example/echo1/{id}/{line_num}/{status.note}",
Method: "GET",
Handler: _HTTP_EchoService_Echo_2,
},
{
Path: "/v1/example/echo2/{no.note}",
Method: "GET",
Handler: _HTTP_EchoService_Echo_3,
},
{
Path: "/v1/example/echo/{id}",
Method: "POST",
Handler: _HTTP_EchoService_Echo_4,
},
{
Path: "/v1/example/echo_body",
Method: "POST",
Handler: _HTTP_EchoService_EchoBody_0,
},
{
Path: "/v1/example/echo_delete",
Method: "DELETE",
Handler: _HTTP_EchoService_EchoDelete_0,
},
{
Path: "/v1/example/echo_patch",
Method: "PATCH",
Handler: _HTTP_EchoService_EchoPatch_0,
},
},
Metadata: "echo_service.proto",
}

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

@ -0,0 +1,101 @@
package main
import (
"bytes"
"html/template"
"strings"
)
var httpTemplate = `
type {{.ServiceType}}HTTPServer interface {
{{range .MethodSets}}
{{.Name}}(context.Context, *{{.Request}}) (*{{.Reply}}, error)
{{end}}
}
func Register{{.ServiceType}}HTTPServer(s http1.ServiceRegistrar, srv {{.ServiceType}}HTTPServer) {
s.RegisterService(&_HTTP_{{.ServiceType}}_serviceDesc, srv)
}
{{range .Methods}}
func _HTTP_{{$.ServiceType}}_{{.Name}}_{{.Num}}(srv interface{}, ctx context.Context, req *http.Request, dec func(interface{}) error, m middleware.Middleware) (interface{}, error) {
var in {{.Request}}
{{if ne (len .Vars) 0}}
if err := http1.BindVars(req, &in); err != nil {
return nil, err
}
{{end}}
{{if eq .Body ""}}
if err := http1.BindForm(req, &in); err != nil {
return nil, err
}
{{else if eq .Body ".*"}}
if err := dec(&in); err != nil {
return nil, err
}
{{else}}
if err := dec(in{{.Body}}); err != nil {
return nil, err
}
{{end}}
h := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.({{$.ServiceType}}Server).{{.Name}}(ctx, &in)
}
out, err := m(h)(ctx, &in)
if err != nil {
return nil, err
}
return out{{.ResponseBody}}, nil
}
{{end}}
var _HTTP_{{.ServiceType}}_serviceDesc = http1.ServiceDesc{
ServiceName: "{{.ServiceName}}",
Methods: []http1.MethodDesc{
{{range .Methods}}
{
Path: "{{.Path}}",
Method: "{{.Method}}",
Handler: _HTTP_{{$.ServiceType}}_{{.Name}}_{{.Num}},
},
{{end}}
},
Metadata: "{{.Metadata}}",
}
`
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
Num int
Vars []string
Forms []string
Request string
Reply string
// http_rule
Path string
Method string
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 string(buf.Bytes())
}

@ -0,0 +1,37 @@
# config
可以指定多个配置源,config 会进行合并成 map[string]interface{},然后通过 Scan 或者 Value 获取值内容;
```
c := config.New(
config.WithSource(
file.NewSource(path),
),
config.WithDecoder(func(kv *config.KeyValue, v map[string]interface{}) error {
// kv.Key
// kv.Value
// kv.Metadata
// 自定义实现对应的数据源解析,如果是配置中心数据源也可以指定metadata进行识别配置类型
return yaml.Unmarshal(kv.Value, v)
}),
)
// 加载配置源:
if err := c.Load(); err != nil {
panic(err)
}
// 获取对应的值内容:
name, err := c.Value("service").String()
// 解析到结构体(由于已经合并到map[string]interface{},所以需要指定 jsonName 进行解析):
var v struct {
Service string `json:"service"`
Version string `json:"version"`
}
if err := c.Scan(&v); err != nil {
panic(err)
}
// 监听值内容变更
c.Watch("service.name", func(key string, value config.Value) {
// 值内容变更
})
```

@ -0,0 +1,141 @@
package config
import (
"encoding/json"
"errors"
"reflect"
"sync"
"time"
"github.com/go-kratos/kratos/v2/log"
)
var (
// ErrNotFound is key not found.
ErrNotFound = errors.New("key not found")
// ErrTypeAssert is type assert error.
ErrTypeAssert = errors.New("type assert error")
_ Config = (*config)(nil)
)
// Observer is config observer.
type Observer func(string, Value)
// Config is a config interface.
type Config interface {
Load() error
Scan(v interface{}) error
Value(key string) Value
Watch(key string, o Observer) error
Close() error
}
type config struct {
opts options
reader Reader
cached sync.Map
observers sync.Map
watchers []Watcher
log *log.Helper
}
// New new a config with options.
func New(opts ...Option) Config {
options := options{
logger: log.DefaultLogger,
decoder: func(kv *KeyValue, v map[string]interface{}) error {
return json.Unmarshal(kv.Value, &v)
},
}
for _, o := range opts {
o(&options)
}
return &config{
opts: options,
reader: newReader(options),
log: log.NewHelper("config", options.logger),
}
}
func (c *config) watch(w Watcher) {
for {
kvs, err := w.Next()
if err != nil {
time.Sleep(time.Second)
c.log.Errorf("Failed to watch next config: %v", err)
continue
}
if err := c.reader.Merge(kvs...); err != nil {
c.log.Errorf("Failed to merge next config: %v", err)
continue
}
c.cached.Range(func(key, value interface{}) bool {
k := key.(string)
v := value.(Value)
if n, ok := c.reader.Value(k); ok && !reflect.DeepEqual(n.Load(), v.Load()) {
v.Store(n.Load())
if o, ok := c.observers.Load(k); ok {
o.(Observer)(k, v)
}
}
return true
})
}
}
func (c *config) Load() error {
for _, src := range c.opts.sources {
kvs, err := src.Load()
if err != nil {
return err
}
if err := c.reader.Merge(kvs...); err != nil {
c.log.Errorf("Failed to merge config source: %v", err)
return err
}
w, err := src.Watch()
if err != nil {
c.log.Errorf("Failed to watch config source: %v", err)
return err
}
go c.watch(w)
}
return nil
}
func (c *config) Value(key string) Value {
if v, ok := c.cached.Load(key); ok {
return v.(Value)
}
if v, ok := c.reader.Value(key); ok {
c.cached.Store(key, v)
return v
}
return &errValue{err: ErrNotFound}
}
func (c *config) Scan(v interface{}) error {
data, err := c.reader.Source()
if err != nil {
return err
}
return json.Unmarshal(data, v)
}
func (c *config) Watch(key string, o Observer) error {
if v := c.Value(key); v.Load() == nil {
return ErrNotFound
}
c.observers.Store(key, o)
return nil
}
func (c *config) Close() error {
for _, w := range c.watchers {
if err := w.Close(); err != nil {
return err
}
}
return nil
}

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

@ -0,0 +1,142 @@
package file
import (
"errors"
"io/ioutil"
"os"
"path/filepath"
"testing"
"time"
"github.com/go-kratos/kratos/v2/config"
)
const (
_testJSON = `
{
"test": {
"settings" : {
"int_key": 1000,
"float_key": 1000.1,
"duration_key": 10000,
"string_key": "string_value"
},
"server": {
"addr": "127.0.0.1",
"port": 8000
}
}
}`
)
func TestFile(t *testing.T) {
var (
path = filepath.Join(os.TempDir(), "test_config")
file = filepath.Join(path, "test.json")
data = []byte(_testJSON)
)
defer os.Remove(path)
if err := os.MkdirAll(path, 0700); err != nil {
t.Error(err)
}
if err := ioutil.WriteFile(file, data, 0666); err != nil {
t.Error(err)
}
testSource(t, file, data)
testSource(t, path, data)
}
func testSource(t *testing.T, path string, data []byte) {
t.Log(path)
s := NewSource(path)
kvs, err := s.Load()
if err != nil {
t.Error(err)
}
if string(kvs[0].Value) != string(data) {
t.Errorf("no expected: %s, but got: %s", kvs[0].Value, data)
}
}
func TestConfig(t *testing.T) {
path := filepath.Join(os.TempDir(), "test_config.json")
defer os.Remove(path)
if err := ioutil.WriteFile(path, []byte(_testJSON), 0666); err != nil {
t.Error(err)
}
c := config.New(config.WithSource(
NewSource(path),
))
testConfig(t, c)
}
func testConfig(t *testing.T, c config.Config) {
var expected = map[string]interface{}{
"test.settings.int_key": int64(1000),
"test.settings.float_key": float64(1000.1),
"test.settings.string_key": "string_value",
"test.settings.duration_key": time.Duration(10000),
"test.server.addr": "127.0.0.1",
"test.server.port": int64(8000),
}
if err := c.Load(); err != nil {
t.Error(err)
}
for key, value := range expected {
switch value.(type) {
case int64:
if v, err := c.Value(key).Int(); err != nil {
t.Error(key, value, err)
} else if v != value {
t.Errorf("no expect key: %s value: %v, but got: %v", key, value, v)
}
case float64:
if v, err := c.Value(key).Float(); err != nil {
t.Error(key, value, err)
} else if v != value {
t.Errorf("no expect key: %s value: %v, but got: %v", key, value, v)
}
case string:
if v, err := c.Value(key).String(); err != nil {
t.Error(key, value, err)
} else if v != value {
t.Errorf("no expect key: %s value: %v, but got: %v", key, value, v)
}
case time.Duration:
if v, err := c.Value(key).Duration(); err != nil {
t.Error(key, value, err)
} else if v != value {
t.Errorf("no expect key: %s value: %v, but got: %v", key, value, v)
}
}
}
// scan
var settings struct {
IntKey int64 `json:"int_key"`
FloatKey float64 `json:"float_key"`
StringKey string `json:"string_key"`
DurationKey time.Duration `json:"duration_key"`
}
if err := c.Value("test.settings").Scan(&settings); err != nil {
t.Error(err)
}
if v := expected["test.settings.int_key"]; settings.IntKey != v {
t.Errorf("no expect int_key value: %v, but got: %v", settings.IntKey, v)
}
if v := expected["test.settings.float_key"]; settings.FloatKey != v {
t.Errorf("no expect float_key value: %v, but got: %v", settings.FloatKey, v)
}
if v := expected["test.settings.string_key"]; settings.StringKey != v {
t.Errorf("no expect string_key value: %v, but got: %v", settings.StringKey, v)
}
if v := expected["test.settings.duration_key"]; settings.DurationKey != v {
t.Errorf("no expect duration_key value: %v, but got: %v", settings.DurationKey, v)
}
// not found
if _, err := c.Value("not_found_key").Bool(); errors.Is(err, config.ErrNotFound) {
t.Logf("not_found_key not match: %v", err)
}
}

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

@ -0,0 +1,38 @@
package config
import (
"github.com/go-kratos/kratos/v2/log"
)
// Decoder is config decoder.
type Decoder func(*KeyValue, map[string]interface{}) error
// Option is config option.
type Option func(*options)
type options struct {
sources []Source
decoder Decoder
logger log.Logger
}
// WithSource with config source.
func WithSource(s ...Source) Option {
return func(o *options) {
o.sources = s
}
}
// WithDecoder with config decoder.
func WithDecoder(d Decoder) Option {
return func(o *options) {
o.decoder = d
}
}
// WithLogger with config loogger.
func WithLogger(l log.Logger) Option {
return func(o *options) {
o.logger = l
}
}

@ -0,0 +1,107 @@
package config
import (
"encoding/json"
"fmt"
"strings"
"github.com/imdario/mergo"
)
// Reader is config reader.
type Reader interface {
Merge(...*KeyValue) error
Value(string) (Value, bool)
Source() ([]byte, error)
}
type reader struct {
opts options
values map[string]interface{}
}
func newReader(opts options) Reader {
return &reader{
opts: opts,
values: make(map[string]interface{}),
}
}
func (r *reader) Merge(kvs ...*KeyValue) error {
merged, err := cloneMap(r.values)
if err != nil {
return err
}
for _, kv := range kvs {
next := make(map[string]interface{})
if err := r.opts.decoder(kv, next); err != nil {
return err
}
if err := mergo.Map(&merged, convertMap(next), mergo.WithOverride); err != nil {
return err
}
}
r.values = merged
return nil
}
func (r *reader) Value(path string) (Value, bool) {
var (
next = r.values
keys = strings.Split(path, ".")
last = len(keys) - 1
)
for idx, key := range keys {
value, ok := next[key]
if !ok {
return nil, false
}
if idx == last {
av := &atomicValue{}
av.Store(value)
return av, true
}
switch vm := value.(type) {
case map[string]interface{}:
next = vm
default:
return nil, false
}
}
return nil, false
}
func (r *reader) Source() ([]byte, error) {
return json.Marshal(r.values)
}
func cloneMap(src map[string]interface{}) (map[string]interface{}, error) {
data, err := json.Marshal(src)
if err != nil {
return nil, err
}
dst := make(map[string]interface{})
if err = json.Unmarshal(data, &dst); err != nil {
return nil, err
}
return dst, nil
}
func convertMap(src interface{}) interface{} {
switch m := src.(type) {
case map[string]interface{}:
dst := make(map[string]interface{}, len(m))
for k, v := range m {
dst[k] = convertMap(v)
}
return dst
case map[interface{}]interface{}:
dst := make(map[string]interface{}, len(m))
for k, v := range m {
dst[fmt.Sprint(k)] = convertMap(v)
}
return dst
default:
return src
}
}

@ -0,0 +1,20 @@
package config
// KeyValue is config key value.
type KeyValue struct {
Key string
Value []byte
Metadata map[string]string
}
// Source is config source.
type Source interface {
Load() ([]*KeyValue, error)
Watch() (Watcher, error)
}
// Watcher watches a source for changes.
type Watcher interface {
Next() ([]*KeyValue, error)
Close() error
}

@ -0,0 +1,105 @@
package config
import (
"encoding/json"
"fmt"
"reflect"
"strconv"
"sync/atomic"
"time"
"google.golang.org/protobuf/encoding/protojson"
"google.golang.org/protobuf/proto"
)
var (
_ Value = (*atomicValue)(nil)
_ Value = (*errValue)(nil)
)
// Value is config value interface.
type Value interface {
Bool() (bool, error)
Int() (int64, error)
Float() (float64, error)
String() (string, error)
Duration() (time.Duration, error)
Scan(interface{}) error
Load() interface{}
Store(interface{})
}
type atomicValue struct {
atomic.Value
}
func (v *atomicValue) Bool() (bool, error) {
switch val := v.Load().(type) {
case bool:
return val, nil
case int64, float64, string:
return strconv.ParseBool(fmt.Sprint(val))
}
return false, fmt.Errorf("type assert to %v failed", reflect.TypeOf(v.Load()))
}
func (v *atomicValue) Int() (int64, error) {
switch val := v.Load().(type) {
case int64:
return int64(val), nil
case float64:
return int64(val), nil
case string:
return strconv.ParseInt(val, 10, 64)
}
return 0, fmt.Errorf("type assert to %v failed", reflect.TypeOf(v.Load()))
}
func (v *atomicValue) Float() (float64, error) {
switch val := v.Load().(type) {
case float64:
return float64(val), nil
case int64:
return float64(val), nil
case string:
return strconv.ParseFloat(val, 10)
}
return 0.0, fmt.Errorf("type assert to %v failed", reflect.TypeOf(v.Load()))
}
func (v *atomicValue) String() (string, error) {
switch val := v.Load().(type) {
case string:
return val, nil
case bool, int64, float64:
return fmt.Sprint(val), nil
}
return "", fmt.Errorf("type assert to %v failed", reflect.TypeOf(v.Load()))
}
func (v *atomicValue) Duration() (time.Duration, error) {
val, err := v.Int()
if err != nil {
return 0, err
}
return time.Duration(val), nil
}
func (v *atomicValue) Scan(obj interface{}) error {
data, err := json.Marshal(v.Load())
if err != nil {
return err
}
if pb, ok := obj.(proto.Message); ok {
return protojson.Unmarshal(data, pb)
}
return json.Unmarshal(data, obj)
}
type errValue struct {
err error
}
func (v errValue) Bool() (bool, error) { return false, v.err }
func (v errValue) Int() (int64, error) { return 0, v.err }
func (v errValue) Float() (float64, error) { return 0.0, v.err }
func (v errValue) Duration() (time.Duration, error) { return 0, v.err }
func (v errValue) String() (string, error) { return "", v.err }
func (v errValue) Scan(interface{}) error { return v.err }
func (v errValue) Load() interface{} { return nil }
func (v errValue) Store(interface{}) {}

@ -1 +0,0 @@
v1.go-kratos.dev

@ -1,21 +0,0 @@
# 安装失败,提示go mod 错误
执行
```shell
go get -u github.com/go-kratos/kratos/tool/kratos
```
出现以下错误时
```shell
go: github.com/prometheus/client_model@v0.0.0-20190220174349-fd36f4220a90: parsing go.mod: missing module line
go: github.com/remyoudompheng/bigfft@v0.0.0-20190806203942-babf20351dd7e3ac320adedbbe5eb311aec8763c: parsing go.mod: missing module line
```
如果你使用了https://goproxy.io/ 代理,那你要使用其他代理来替换它,然后删除GOPATH目录下的mod缓存文件夹(`go clean --modcache`),然后重新执行安装命令
代理列表
```
export GOPROXY=https://mirrors.aliyun.com/goproxy/
export GOPROXY=https://goproxy.cn/
export GOPROXY=https://goproxy.io/
```

@ -1,36 +0,0 @@
![kratos](img/kratos3.png)
# Kratos
Kratos是bilibili开源的一套Go微服务框架,包含大量微服务相关框架及工具。
### Goals
我们致力于提供完整的微服务研发体验,整合相关框架及工具后,微服务治理相关部分可对整体业务开发周期无感,从而更加聚焦于业务交付。对每位开发者而言,整套Kratos框架也是不错的学习仓库,可以了解和参考到bilibili在微服务方面的技术积累和经验。
### Principles
* 简单:不过度设计,代码平实简单
* 通用:通用业务开发所需要的基础库的功能
* 高效:提高业务迭代的效率
* 稳定:基础库可测试性高,覆盖率高,有线上实践安全可靠
* 健壮:通过良好的基础库设计,减少错用
* 高性能:性能高,但不特定为了性能做hack优化,引入unsafe
* 扩展性:良好的接口设计,来扩展实现,或者通过新增基础库目录来扩展功能
* 容错性:为失败设计,大量引入对SRE的理解,鲁棒性高
* 工具链:包含大量工具链,比如cache代码生成,lint工具等等
### Features
* HTTP Blademaster:核心基于[gin](https://github.com/gin-gonic/gin)进行模块化设计,简单易用、核心足够轻量;
* GRPC Warden:基于官方gRPC开发,集成[discovery](https://github.com/bilibili/discovery)服务发现,并融合P2C负载均衡;
* Cache:优雅的接口化设计,非常方便的缓存序列化,推荐结合代理模式[overlord](https://github.com/bilibili/overlord);
* Database:集成MySQL/HBase/TiDB,添加熔断保护和统计支持,可快速发现数据层压力;
* Config:方便易用的[paladin sdk](config-paladin.md),可配合远程配置中心,实现配置版本管理和更新;
* Log:类似[zap](https://github.com/uber-go/zap)的field实现高性能日志库,并结合log-agent实现远程日志管理;
* Trace:基于opentracing,集成了全链路trace支持(gRPC/HTTP/MySQL/Redis/Memcached);
* Kratos Tool:工具链,可快速生成标准项目,或者通过Protobuf生成代码,非常便捷使用gRPC、HTTP、swagger文档;
-------------
> 名字来源于:《战神》游戏以希腊神话为背景,讲述由凡人成为战神的奎托斯(Kratos)成为战神并展开弑神屠杀的冒险历程。

@ -1,39 +0,0 @@
* [介绍](README.md)
* [快速开始 - 项目初始化](quickstart.md)
* [FAQ](FAQ.md)
* [http blademaster](blademaster.md)
* [bm quickstart](blademaster-quickstart.md)
* [bm module](blademaster-mod.md)
* [bm middleware](blademaster-mid.md)
* [bm protobuf](blademaster-pb.md)
* [grpc warden](warden.md)
* [warden quickstart](warden-quickstart.md)
* [warden interceptor](warden-mid.md)
* [warden resolver](warden-resolver.md)
* [warden balancer](warden-balancer.md)
* [warden protobuf](warden-pb.md)
* [config](config.md)
* [paladin](config-paladin.md)
* [ecode](ecode.md)
* [trace](trace.md)
* [log](logger.md)
* [log-agent](log-agent.md)
* [database](database.md)
* [mysql](database-mysql.md)
* [mysql-orm](database-mysql-orm.md)
* [hbase](database-hbase.md)
* [tidb](database-tidb.md)
* [cache](cache.md)
* [memcache](cache-mc.md)
* [redis](cache-redis.md)
* [kratos工具](kratos-tool.md)
* [protoc](kratos-protoc.md)
* [swagger](kratos-swagger.md)
* [genmc](kratos-genmc.md)
* [genbts](kratos-genbts.md)
* [限流bbr](ratelimit.md)
* [熔断breaker](breaker.md)
* [UT单元测试](ut.md)
* [testcli UT运行环境构建工具](ut-testcli.md)
* [testgen UT代码自动生成器](ut-testgen.md)
* [support UT周边辅助工具](ut-support.md)

@ -1,177 +0,0 @@
# 背景
基于bm的handler机制,可以自定义很多middleware(中间件)进行通用的业务处理,比如用户登录鉴权。接下来就以鉴权为例,说明middleware的写法和用法。
# 写自己的中间件
middleware本质上就是一个handler,接口和方法声明如下代码:
```go
// Handler responds to an HTTP request.
type Handler interface {
ServeHTTP(c *Context)
}
// HandlerFunc http request handler function.
type HandlerFunc func(*Context)
// ServeHTTP calls f(ctx).
func (f HandlerFunc) ServeHTTP(c *Context) {
f(c)
}
```
1. 实现了`Handler`接口,可以作为engine的全局中间件使用:`engine.Use(YourHandler)`
2. 声明为`HandlerFunc`方法,可以作为engine的全局中间件使用:`engine.UseFunc(YourHandlerFunc)`,也可以作为router的局部中间件使用:`e.GET("/path", YourHandlerFunc)`
简单示例代码如下:
```go
type Demo struct {
Key string
Value string
}
// ServeHTTP implements from Handler interface
func (d *Demo) ServeHTTP(ctx *bm.Context) {
ctx.Set(d.Key, d.Value)
}
e := bm.DefaultServer(nil)
d := &Demo{}
// Handler使用如下:
e.Use(d)
// HandlerFunc使用如下:
e.UseFunc(d.ServeHTTP)
e.GET("/path", d.ServeHTTP)
// 或者只有方法
myHandler := func(ctx *bm.Context) {
// some code
}
e.UseFunc(myHandler)
e.GET("/path", myHandler)
```
# 全局中间件
在blademaster的`server.go`代码中,有以下代码:
```go
func DefaultServer(conf *ServerConfig) *Engine {
engine := NewServer(conf)
engine.Use(Recovery(), Trace(), Logger())
return engine
}
```
会默认创建一个`bm engine`,并注册`Recovery(), Trace(), Logger()`三个middlerware用于全局handler处理,优先级从前到后。如果想要将自定义的middleware注册进全局,可以继续调用Use方法如下:
```go
engine.Use(YourMiddleware())
```
此方法会将`YourMiddleware`追加到已有的全局middleware后执行。如果需要全部自定义全局执行的middleware,可以使用`NewServer`方法创建一个无middleware的engine对象,然后使用`engine.Use/UseFunc`进行注册。
# 局部中间件
先来看一段鉴权伪代码示例([auth示例代码位置](https://github.com/go-kratos/kratos/tree/master/example/blademaster/middleware/auth)):
```go
func Example() {
myHandler := func(ctx *bm.Context) {
mid := metadata.Int64(ctx, metadata.Mid)
ctx.JSON(fmt.Sprintf("%d", mid), nil)
}
authn := auth.New(&auth.Config{DisableCSRF: false})
e := bm.DefaultServer(nil)
// "/user"接口必须保证登录用户才能访问,那么我们加入"auth.User"来确保用户鉴权通过,才能进入myHandler进行业务逻辑处理
e.GET("/user", authn.User, myHandler)
// "/guest"接口访客用户就可以访问,但如果登录用户我们需要知道mid,那么我们加入"auth.Guest"来尝试鉴权获取mid,但肯定会继续执行myHandler进行业务逻辑处理
e.GET("/guest", authn.Guest, myHandler)
// "/owner"开头的所有接口,都需要进行登录鉴权才可以被访问,那可以创建一个group并加入"authn.User"
o := e.Group("/owner", authn.User)
o.GET("/info", myHandler) // 该group创建的router不需要再显示的加入"authn.User"
o.POST("/modify", myHandler) // 该group创建的router不需要再显示的加入"authn.User"
e.Start()
}
```
# 内置中间件
## Recovery
代码位于`pkg/net/http/blademaster/recovery.go`内,用于recovery panic。会被`DefaultServer`默认注册,建议使用`NewServer`的话也将其作为首个中间件注册。
## Trace
代码位于`pkg/net/http/blademaster/trace.go`内,用于trace设置,并且实现了`net/http/httptrace`的接口,能够收集官方库内的调用栈详情。会被`DefaultServer`默认注册,建议使用`NewServer`的话也将其作为第二个中间件注册。
## Logger
代码位于`pkg/net/http/blademaster/logger.go`内,用于请求日志记录。会被`DefaultServer`默认注册,建议使用`NewServer`的话也将其作为第三个中间件注册。
## CSRF
代码位于`pkg/net/http/blademaster/csrf.go`内,用于防跨站请求。如要使用如下:
```go
e := bm.DefaultServer(nil)
// 挂载自适应限流中间件到 bm engine,使用默认配置
csrf := bm.CSRF([]string{"bilibili.com"}, []string{"/a/api"})
e.Use(csrf)
// 或者
e.GET("/api", csrf, myHandler)
```
## CORS
代码位于`pkg/net/http/blademaster/cors.go`内,用于跨域允许请求。请注意该:
1. 使用该中间件进行全局注册后,可"省略"单独为`OPTIONS`请求注册路由,如示例一。
2. 使用该中间单独为某路由注册,需要为该路由再注册一个`OPTIONS`方法的同路径路由,如示例二。
示例一:
```go
e := bm.DefaultServer(nil)
// 挂载自适应限流中间件到 bm engine,使用默认配置
cors := bm.CORS([]string{"github.com"})
e.Use(cors)
// 该路由可以默认针对 OPTIONS /api 的跨域请求支持
e.POST("/api", myHandler)
```
示例二:
```go
e := bm.DefaultServer(nil)
// 挂载自适应限流中间件到 bm engine,使用默认配置
cors := bm.CORS([]string{"github.com"})
// e.Use(cors) 不进行全局注册
e.OPTIONS("/api", cors, myHandler) // 需要单独为/api进行OPTIONS方法注册
e.POST("/api", cors, myHandler)
```
## 自适应限流
更多关于自适应限流的信息可参考:[kratos 自适应限流](ratelimit.md)。如要使用如下:
```go
e := bm.DefaultServer(nil)
// 挂载自适应限流中间件到 bm engine,使用默认配置
limiter := bm.NewRateLimiter(nil)
e.Use(limiter.Limit())
// 或者
e.GET("/api", csrf, myHandler)
```
# 扩展阅读
[bm快速开始](blademaster-quickstart.md)
[bm模块说明](blademaster-mod.md)
[bm基于pb生成](blademaster-pb.md)

@ -1,88 +0,0 @@
# Context
以下是 blademaster 中 Context 对象结构体声明的代码片段:
```go
// Context is the most important part. It allows us to pass variables between
// middleware, manage the flow, validate the JSON of a request and render a
// JSON response for example.
type Context struct {
context.Context
Request *http.Request
Writer http.ResponseWriter
// flow control
index int8
handlers []HandlerFunc
// Keys is a key/value pair exclusively for the context of each request.
Keys map[string]interface{}
Error error
method string
engine *Engine
}
```
* 首先可以看到 blademaster 的 Context 结构体中会 embed 一个标准库中的 Context 实例,bm 中的 Context 也是直接通过该实例来实现标准库中的 Context 接口。
* blademaster 会使用配置的 server timeout (默认1s) 作为一次请求整个过程中的超时时间,使用该context调用dao做数据库、缓存操作查询时均会将该超时时间传递下去,一旦抵达deadline,后续相关操作均会返回`context deadline exceeded`。
* Request 和 Writer 字段用于获取当前请求的与输出响应。
* index 和 handlers 用于 handler 的流程控制;handlers 中存储了当前请求需要执行的所有 handler,index 用于标记当前正在执行的 handler 的索引位。
* Keys 用于在 handler 之间传递一些额外的信息。
* Error 用于存储整个请求处理过程中的错误。
* method 用于检查当前请求的 Method 是否与预定义的相匹配。
* engine 字段指向当前 blademaster 的 Engine 实例。
以下为 Context 中所有的公开的方法:
```go
// 用于 Handler 的流程控制
func (c *Context) Abort()
func (c *Context) AbortWithStatus(code int)
func (c *Context) Bytes(code int, contentType string, data ...[]byte)
func (c *Context) IsAborted() bool
func (c *Context) Next()
// 用户获取或者传递请求的额外信息
func (c *Context) RemoteIP() (cip string)
func (c *Context) Set(key string, value interface{})
func (c *Context) Get(key string) (value interface{}, exists bool)
// 用于校验请求的 payload
func (c *Context) Bind(obj interface{}) error
func (c *Context) BindWith(obj interface{}, b binding.Binding) error
// 用于输出响应
func (c *Context) Render(code int, r render.Render)
func (c *Context) Redirect(code int, location string)
func (c *Context) Status(code int)
func (c *Context) String(code int, format string, values ...interface{})
func (c *Context) XML(data interface{}, err error)
func (c *Context) JSON(data interface{}, err error)
func (c *Context) JSONMap(data map[string]interface{}, err error)
func (c *Context) Protobuf(data proto.Message, err error)
```
所有方法基本上可以分为三类:
* 流程控制
* 额外信息传递
* 请求处理
* 响应处理
# Handler
![handler](img/bm-handlers.png)
初次接触`blademaster`的用户可能会对其`Handler`的流程处理产生不小的疑惑,实际上`bm`对`Handler`对处理非常简单:
* 将`Router`模块中预先注册的`middleware`与其他`Handler`合并,放入`Context`的`handlers`字段,并将`index`字段置`0`
* 然后通过`Next()`方法一个个执行下去,部分`middleware`可能想要在过程中中断整个流程,此时可以使用`Abort()`方法提前结束处理
* 有些`middleware`还想在所有`Handler`执行完后再执行部分逻辑,此时可以在自身`Handler`中显式调用`Next()`方法,并将这些逻辑放在调用了`Next()`方法之后
# 扩展阅读
[bm快速开始](blademaster-quickstart.md)
[bm中间件](blademaster-mid.md)
[bm基于pb生成](blademaster-pb.md)

@ -1,83 +0,0 @@
# 介绍
基于proto文件可以快速生成`bm`框架对应的代码,提前需要准备以下工作:
* 安装`kratos tool protoc`工具,请看[kratos工具](kratos-tool.md)
* 编写`proto`文件,示例可参考[kratos-demo内proto文件](https://github.com/go-kratos/kratos-demo/blob/master/api/api.proto)
### kratos工具说明
`kratos tool protoc`工具可以生成`warden` `bm` `swagger`对应的代码和文档,想要单独生成`bm`代码只需加上`--bm`如:
```shell
# generate BM HTTP
kratos tool protoc --bm api.proto
```
### proto文件说明
请注意想要生成`bm`代码,需要特别在`proto`的`service`内指定`google.api.http`配置,如下:
```go
service Demo {
rpc SayHello (HelloReq) returns (.google.protobuf.Empty);
rpc SayHelloURL(HelloReq) returns (HelloResp) {
option (google.api.http) = { // 该配置指定SayHelloURL方法对应的url
get:"/kratos-demo/say_hello" // 指定url和请求方式为GET
};
};
}
```
# 使用
建议在项目`api`目录下编写`proto`文件及生成对应的代码,可参考[kratos-demo内的api目录](https://github.com/go-kratos/kratos-demo/tree/master/api)。
执行命令后生成的`api.bm.go`代码,注意其中的`type DemoBMServer interface`和`RegisterDemoBMServer`,其中:
* `DemoBMServer`接口,包含`proto`文件内配置了`google.api.http`选项的所有方法
* `RegisterDemoBMServer`方法提供注册`DemoBMServer`接口的实现对象,和`bm`的`Engine`用于注册路由
* `DemoBMServer`接口的实现,一般为`internal/service`内的业务逻辑代码,需要实现`DemoBMServer`接口
使用`RegisterDemoBMServer`示例代码请参考[kratos-demo内的http](https://github.com/go-kratos/kratos-demo/blob/master/internal/server/http/server.go)内的如下代码:
```go
engine = bm.DefaultServer(hc.Server)
pb.RegisterDemoBMServer(engine, svc)
initRouter(engine)
```
`internal/service`内的`Service`结构实现了`DemoBMServer`接口可参考[kratos-demo内的service](https://github.com/go-kratos/kratos-demo/blob/master/internal/service/service.go)内的如下代码:
```go
// SayHelloURL bm demo func.
func (s *Service) SayHelloURL(ctx context.Context, req *pb.HelloReq) (reply *pb.HelloResp, err error) {
reply = &pb.HelloResp{
Content: "hello " + req.Name,
}
fmt.Printf("hello url %s", req.Name)
return
}
```
# 文档
基于同一份`proto`文件还可以生成对应的`swagger`文档,运行命令如下:
```shell
# generate swagger
kratos tool protoc --swagger api.proto
```
该命令将生成对应的`swagger.json`文件,可用于`swagger`工具通过WEBUI的方式打开使用,可运行命令如下:
```shell
kratos tool swagger serve api/api.swagger.json
```
# 扩展阅读
[bm快速开始](blademaster-quickstart.md)
[bm模块说明](blademaster-mod.md)
[bm中间件](blademaster-mid.md)

@ -1,142 +0,0 @@
# 路由
进入`internal/server/http`目录下,打开`http.go`文件,其中有默认生成的`blademaster`模板。其中:
```go
engine = bm.DefaultServer(hc.Server)
initRouter(engine)
if err := engine.Start(); err != nil {
panic(err)
}
```
是bm默认创建的`engine`及启动代码,我们看`initRouter`初始化路由方法,默认实现了:
```go
func initRouter(e *bm.Engine) {
e.Ping(ping) // engine自带的"/ping"接口,用于负载均衡检测服务健康状态
g := e.Group("/kratos-demo") // e.Group 创建一组 "/kratos-demo" 起始的路由组
{
g.GET("/start", howToStart) // g.GET 创建一个 "kratos-demo/start" 的路由,使用GET方式请求,默认处理Handler为howToStart方法
g.POST("start", howToStart) // g.POST 创建一个 "kratos-demo/start" 的路由,使用POST方式请求,默认处理Handler为howToStart方法
}
}
```
bm的handler方法,结构如下:
```go
func howToStart(c *bm.Context) // handler方法默认传入bm的Context对象
```
### Ping
engine自带Ping方法,用于设置`/ping`路由的handler,该路由统一提供于负载均衡服务做健康检测。服务是否健康,可自定义`ping handler`进行逻辑判断,如检测DB是否正常等。
```go
func ping(c *bm.Context) {
if some DB check not ok {
c.AbortWithStatus(503)
}
}
```
# 默认路由
默认路由有:
* /metrics 用于prometheus信息采集
* /metadata 可以查看所有注册的路由信息
查看加载的所有路由信息:
```shell
curl 'http://127.0.0.1:8000/metadata'
```
输出:
```json
{
"code": 0,
"message": "0",
"ttl": 1,
"data": {
"/kratos-demo/start": {
"method": "GET"
},
"/metadata": {
"method": "GET"
},
"/metrics": {
"method": "GET"
},
"/ping": {
"method": "GET"
}
}
}
```
# 路径参数
使用方式如下:
```go
func initRouter(e *bm.Engine) {
e.Ping(ping)
g := e.Group("/kratos-demo")
{
g.GET("/start", howToStart)
// 路径参数有两个特殊符号":"和"*"
// ":" 跟在"/"后面为参数的key,匹配两个/中间的值 或 一个/到结尾(其中不再包含/)的值
// "*" 跟在"/"后面为参数的key,匹配从 /*开始到结尾的所有值,所有*必须写在最后且无法多个
// NOTE:这是不被允许的,会和 /start 冲突
// g.GET("/:xxx")
// NOTE: 可以拿到一个key为name的参数。注意只能匹配到/param1/felix,无法匹配/param1/felix/hao(该路径会404)
g.GET("/param1/:name", pathParam)
// NOTE: 可以拿到多个key参数。注意只能匹配到/param2/felix/hao/love,无法匹配/param2/felix或/param2/felix/hao
g.GET("/param2/:name/:value/:felid", pathParam)
// NOTE: 可以拿到一个key为name的参数 和 一个key为action的路径。
// NOTE: 如/params3/felix/hello,action的值为"/hello"
// NOTE: 如/params3/felix/hello/hi,action的值为"/hello/hi"
// NOTE: 如/params3/felix/hello/hi/,action的值为"/hello/hi/"
g.GET("/param3/:name/*action", pathParam)
}
}
func pathParam(c *bm.Context) {
name, _ := c.Params.Get("name")
value, _ := c.Params.Get("value")
felid, _ := c.Params.Get("felid")
action, _ := c.Params.Get("action")
path := c.RoutePath // NOTE: 获取注册的路由原始地址,如: /kratos-demo/param1/:name
c.JSONMap(map[string]interface{}{
"name": name,
"value": value,
"felid": felid,
"action": action,
"path": path,
}, nil)
}
```
# 性能分析
启动时默认监听了`2333`端口用于`pprof`信息采集,如:
```shell
go tool pprof http://127.0.0.1:8000/debug/pprof/profile
```
改变端口可以使用flag,如:`-http.perf=tcp://0.0.0.0:12333`
# 扩展阅读
[bm模块说明](blademaster-mod.md)
[bm中间件](blademaster-mid.md)
[bm基于pb生成](blademaster-pb.md)

@ -1,43 +0,0 @@
# 背景
在像微服务这样的分布式架构中,经常会有一些需求需要你调用多个服务,但是还需要确保服务的安全性、统一化每次的请求日志或者追踪用户完整的行为等等。要实现这些功能,你可能需要在所有服务中都设置一些相同的属性,虽然这个可以通过一些明确的接入文档来描述或者准入规范来界定,但是这么做的话还是有可能会有一些问题:
1. 你很难让每一个服务都实现上述功能。因为对于开发者而言,他们应当注重的是实现功能。很多项目的开发者经常在一些日常开发中遗漏了这些关键点,经常有人会忘记去打日志或者去记录调用链。但是对于一些大流量的互联网服务而言,一个线上服务一旦发生故障时,即使故障时间很小,其影响面会非常大。一旦有人在关键路径上忘记路记录日志,那么故障的排除成本会非常高,那样会导致影响面进一步扩大。
2. 事实上实现之前叙述的这些功能的成本也非常高。比如说对于鉴权(Identify)这个功能,你要是去一个服务一个服务地去实现,那样的成本也是非常高的。如果说把这个确保认证的责任分担在每个开发者身上,那样其实也会增加大家遗忘或者忽略的概率。
为了解决这样的问题,你可能需要一个框架来帮助你实现这些功能。比如说帮你在一些关键路径的请求上配置必要的鉴权或超时策略。那样服务间的调用会被多层中间件所过滤并检查,确保整体服务的稳定性。
# 设计目标
* 性能优异,不应该掺杂太多业务逻辑的成分
* 方便开发使用,开发对接的成本应该尽可能地小
* 后续鉴权、认证等业务逻辑的模块应该可以通过业务模块的开发接入该框架内
* 默认配置已经是 production ready 的配置,减少开发与线上环境的差异性
# 概览
* 参考`gin`设计整套HTTP框架,去除`gin`中不需要的部分逻辑
* 内置一些必要的中间件,便于业务方可以直接上手使用
# blademaster架构
![bm-arch](img/bm-arch-2-2.png)
`blademaster`由几个非常精简的内部模块组成。其中`Router`用于根据请求的路径分发请求,`Context`包含了一个完整的请求信息,`Handler`则负责处理传入的`Context`,`Handlers`为一个列表,一个串一个地执行。
所有的`middlerware`均以`Handler`的形式存在,这样可以保证`blademaster`自身足够精简且扩展性足够强。
![bm-arch](img/bm-arch-2-3.png)
`blademaster`处理请求的模式非常简单,大部分的逻辑都被封装在了各种`Handler`中。一般而言,业务逻辑作为最后一个`Handler`。
正常情况下每个`Handler`按照顺序一个一个串行地执行下去,但是`Handler`中也可以中断整个处理流程,直接输出`Response`。这种模式常被用于校验登陆的`middleware`中:一旦发现请求不合法,直接响应拒绝。
请求处理的流程中也可以使用`Render`来辅助渲染`Response`,比如对于不同的请求需要响应不同的数据格式`JSON`、`XML`,此时可以使用不同的`Render`来简化逻辑。
# 扩展阅读
[bm快速开始](blademaster-quickstart.md)
[bm模块说明](blademaster-mod.md)
[bm中间件](blademaster-mid.md)
[bm基于pb生成](blademaster-pb.md)

@ -1,49 +0,0 @@
## 熔断器/Breaker
熔断器是为了当依赖的服务已经出现故障时,主动阻止对依赖服务的请求。保证自身服务的正常运行不受依赖服务影响,防止雪崩效应。
## kratos内置breaker的组件
一般情况下直接使用kratos的组件时都自带了熔断逻辑,并且在提供了对应的breaker配置项。
目前在kratos内集成熔断器的组件有:
- RPC client: pkg/net/rpc/warden/client
- Mysql client:pkg/database/sql
- Tidb client:pkg/database/tidb
- Http client:pkg/net/http/blademaster
## 使用说明
```go
//初始化熔断器组
//一组熔断器公用同一个配置项,可从分组内取出单个熔断器使用。可用在比如mysql主从分离等场景。
brkGroup := breaker.NewGroup(&breaker.Config{})
//为每一个连接指定一个breaker
//此处假设一个客户端连接对象实例为conn
//breakName定义熔断器名称 一般可以使用连接地址
breakName = conn.Addr
conn.breaker = brkGroup.Get(breakName)
//在连接发出请求前判断熔断器状态
if err = conn.breaker.Allow(); err != nil {
return
}
//连接执行成功或失败将结果告知breaker
if(respErr != nil){
conn.breaker.MarkFailed()
}else{
conn.breaker.MarkSuccess()
}
```
## 配置说明
```go
type Config struct {
SwitchOff bool // 熔断器开关,默认关 false.
K float64 //触发熔断的错误率(K = 1 - 1/错误率)
Window xtime.Duration //统计桶窗口时间
Bucket int //统计桶大小
Request int64 //触发熔断的最少请求数量(请求少于该值时不会触发熔断)
}
```

@ -1,195 +0,0 @@
# 开始使用
## 配置
进入项目中的configs目录,打开memcache.toml,我们可以看到:
```toml
[Client]
name = "demo"
proto = "tcp"
addr = "127.0.0.1:11211"
active = 50
idle = 10
dialTimeout = "100ms"
readTimeout = "200ms"
writeTimeout = "300ms"
idleTimeout = "80s"
```
在该配置文件中我们可以配置memcache的连接方式proto、连接地址addr、连接池的闲置连接数idle、最大连接数active以及各类超时。
## 初始化
进入项目的internal/dao目录,打开mc.go,其中:
```go
var cfg struct {
Client *memcache.Config
}
checkErr(paladin.Get("memcache.toml").UnmarshalTOML(&mc))
```
使用paladin配置管理工具将上文中的memcache.toml中的配置解析为我们需要使用的配置。
```go
// dao dao.
type dao struct {
mc *memcache.Memcache
mcExpire int32
}
```
在dao的主结构提中定义了memcache的连接池对象和过期时间。
```go
d = &dao{
// memcache
mc: memcache.New(mc.Demo),
mcExpire: int32(time.Duration(mc.DemoExpire) / time.Second),
}
```
使用kratos/pkg/cache/memcache包的New方法进行连接池对象的初始化,需要传入上文解析的配置。
## Ping
```go
// Ping ping the resource.
func (d *dao) Ping(ctx context.Context) (err error) {
return d.pingMC(ctx)
}
func (d *dao) pingMC(ctx context.Context) (err error) {
if err = d.mc.Set(ctx, &memcache.Item{Key: "ping", Value: []byte("pong"), Expiration: 0}); err != nil {
log.Error("conn.Set(PING) error(%v)", err)
}
return
}
```
生成的dao层模板中自带了memcache相关的ping方法,用于为负载均衡服务的健康监测提供依据,详见[blademaster](blademaster-quickstart.md)。
## 关闭
```go
// Close close the resource.
func (d *Dao) Close() {
d.mc.Close()
}
```
在关闭dao层时,通过调用memcache连接池对象的Close方法,我们可以关闭该连接池,从而释放相关资源。
# 常用方法
推荐使用[memcache代码生成器](kratos-genmc.md)帮助我们生成memcache操作的相关代码。
以下我们来逐一解析以下kratos/pkg/cache/memcache包中提供的常用方法。
## 单个查询
```go
// CacheDemo get data from mc
func (d *Dao) CacheDemo(c context.Context, id int64) (res *Demo, err error) {
key := demoKey(id)
res = &Demo{}
if err = d.mc.Get(c, key).Scan(res); err != nil {
res = nil
if err == memcache.ErrNotFound {
err = nil
}
}
if err != nil {
prom.BusinessErrCount.Incr("mc:CacheDemo")
log.Errorv(c, log.KV("CacheDemo", fmt.Sprintf("%+v", err)), log.KV("key", key))
return
}
return
}
```
如上为代码生成器生成的进行单个查询的代码,使用到mc.Get(c,key)方法获得返回值,再使用scan方法将memcache的返回值转换为golang中的类型(如string,bool, 结构体等)。
## 批量查询使用
```go
replies, err := d.mc.GetMulti(c, keys)
for _, key := range replies.Keys() {
v := &Demo{}
err = replies.Scan(key, v)
}
```
如上为代码生成器生成的进行批量查询的代码片段,这里使用到mc.GetMulti(c,keys)方法获得返回值,与单个查询类似地,我们需要再使用scan方法将memcache的返回值转换为我们定义的结构体。
## 设置KV
```go
// AddCacheDemo Set data to mc
func (d *Dao) AddCacheDemo(c context.Context, id int64, val *Demo) (err error) {
if val == nil {
return
}
key := demoKey(id)
item := &memcache.Item{Key: key, Object: val, Expiration: d.demoExpire, Flags: memcache.FlagJSON | memcache.FlagGzip}
if err = d.mc.Set(c, item); err != nil {
prom.BusinessErrCount.Incr("mc:AddCacheDemo")
log.Errorv(c, log.KV("AddCacheDemo", fmt.Sprintf("%+v", err)), log.KV("key", key))
return
}
return
}
```
如上为代码生成器生成的添加结构体进入memcache的代码,这里需要使用到的是mc.Set方法进行设置。
这里使用的item为memcache.Item结构体,包含key, value, 超时时间(秒), Flags。
### Flags
上文添加结构体进入memcache中,使用到的flags为:memcache.FlagJSON | memcache.FlagGzip代表着:使用json作为编码方式,gzip作为压缩方式。
Flags的相关常量在kratos/pkg/cache/memcache包中进行定义,包含编码方式如gob, json, protobuf,和压缩方式gzip。
```go
const(
// Flag, 15(encoding) bit+ 17(compress) bit
// FlagRAW default flag.
FlagRAW = uint32(0)
// FlagGOB gob encoding.
FlagGOB = uint32(1) << 0
// FlagJSON json encoding.
FlagJSON = uint32(1) << 1
// FlagProtobuf protobuf
FlagProtobuf = uint32(1) << 2
// FlagGzip gzip compress.
FlagGzip = uint32(1) << 15
)
```
## 删除KV
```go
// DelCacheDemo delete data from mc
func (d *Dao) DelCacheDemo(c context.Context, id int64) (err error) {
key := demoKey(id)
if err = d.mc.Delete(c, key); err != nil {
if err == memcache.ErrNotFound {
err = nil
return
}
prom.BusinessErrCount.Incr("mc:DelCacheDemo")
log.Errorv(c, log.KV("DelCacheDemo", fmt.Sprintf("%+v", err)), log.KV("key", key))
return
}
return
}
```
如上为代码生成器生成的从memcache中删除KV的代码,这里需要使用到的是mc.Delete方法。
和查询时类似地,当memcache中不存在参数中的key时,会返回error为memcache.ErrNotFound。如果不需要处理这种error,可以参考上述代码将返回出去的error置为nil。
# 扩展阅读
[memcache代码生成器](kratos-genmc.md)
[redis模块说明](cache-redis.md)

@ -1,181 +0,0 @@
# 开始使用
## 配置
进入项目中的configs目录,打开redis.toml,我们可以看到:
```toml
[Client]
name = "kratos-demo"
proto = "tcp"
addr = "127.0.0.1:6389"
idle = 10
active = 10
dialTimeout = "1s"
readTimeout = "1s"
writeTimeout = "1s"
idleTimeout = "10s"
```
在该配置文件中我们可以配置redis的连接方式proto、连接地址addr、连接池的闲置连接数idle、最大连接数active以及各类超时。
## 初始化
进入项目的internal/dao目录,打开redis.go,其中:
```go
var cfg struct {
Client *memcache.Config
}
checkErr(paladin.Get("redis.toml").UnmarshalTOML(&rc))
```
使用paladin配置管理工具将上文中的redis.toml中的配置解析为我们需要使用的配置。
```go
// Dao dao.
type Dao struct {
redis *redis.Pool
redisExpire int32
}
```
在dao的主结构提中定义了redis的连接池对象和过期时间。
```go
d = &dao{
// redis
redis: redis.NewPool(rc.Demo),
redisExpire: int32(time.Duration(rc.DemoExpire) / time.Second),
}
```
使用kratos/pkg/cache/redis包的NewPool方法进行连接池对象的初始化,需要传入上文解析的配置。
## Ping
```go
// Ping ping the resource.
func (d *dao) Ping(ctx context.Context) (err error) {
return d.pingRedis(ctx)
}
func (d *dao) pingRedis(ctx context.Context) (err error) {
conn := d.redis.Get(ctx)
defer conn.Close()
if _, err = conn.Do("SET", "ping", "pong"); err != nil {
log.Error("conn.Set(PING) error(%v)", err)
}
return
}
```
生成的dao层模板中自带了redis相关的ping方法,用于为负载均衡服务的健康监测提供依据,详见[blademaster](blademaster-quickstart.md)。
## 关闭
```go
// Close close the resource.
func (d *Dao) Close() {
d.redis.Close()
}
```
在关闭dao层时,通过调用redis连接池对象的Close方法,我们可以关闭该连接池,从而释放相关资源。
# 常用方法
## 发送单个命令 Do
```go
// DemoIncrby .
func (d *dao) DemoIncrby(c context.Context, pid int) (err error) {
cacheKey := keyDemo(pid)
conn := d.redis.Get(c)
defer conn.Close()
if _, err = conn.Do("INCRBY", cacheKey, 1); err != nil {
log.Error("DemoIncrby conn.Do(INCRBY) key(%s) error(%v)", cacheKey, err)
}
return
}
```
如上为向redis server发送单个命令的用法示意。这里需要使用redis连接池的Get方法获取一个redis连接conn,再使用conn.Do方法即可发送一条指令。
注意,在使用该连接完毕后,需要使用conn.Close方法将该连接关闭。
## 批量发送命令 Pipeline
kratos/pkg/cache/redis包除了支持发送单个命令,也支持批量发送命令(redis pipeline),比如:
```go
// DemoIncrbys .
func (d *dao) DemoIncrbys(c context.Context, pid int) (err error) {
cacheKey := keyDemo(pid)
conn := d.redis.Get(c)
defer conn.Close()
if err = conn.Send("INCRBY", cacheKey, 1); err != nil {
return
}
if err = conn.Send("EXPIRE", cacheKey, d.redisExpire); err != nil {
return
}
if err = conn.Flush(); err != nil {
log.Error("conn.Flush error(%v)", err)
return
}
for i := 0; i < 2; i++ {
if _, err = conn.Receive(); err != nil {
log.Error("conn.Receive error(%v)", err)
return
}
}
return
}
```
和发送单个命令类似地,这里需要使用redis连接池的Get方法获取一个redis连接conn,在使用该连接完毕后,需要使用conn.Close方法将该连接关闭。
这里使用conn.Send方法将命令写入客户端的buffer(缓冲区)中,使用conn.Flush将客户端的缓冲区内的命令打包发送到redis server。redis server按顺序返回的reply可以使用conn.Receive方法进行接收和处理。
## 返回值转换
kratos/pkg/cache/redis包中也提供了Scan方法将redis server的返回值转换为golang类型。
除此之外,kratos/pkg/cache/redis包提供了大量返回值转换的快捷方式:
### 单个查询
单个查询可以使用redis.Uint64/Int64/Float64/Int/String/Bool/Bytes进行返回值的转换,比如:
```go
// GetDemo get
func (d *Dao) GetDemo(ctx context.Context, key string) (string, error) {
conn := d.redis.Get(ctx)
defer conn.Close()
return redis.String(conn.Do("GET", key))
}
```
### 批量查询
批量查询时候,可以使用redis.Int64s,Ints,Strings,ByteSlices方法转换如MGET,HMGET,ZRANGE,SMEMBERS等命令的返回值。
还可以使用StringMap, IntMap, Int64Map方法转换HGETALL命令的返回值,比如:
```go
// HGETALLDemo get
func (d *Dao) HGETALLDemo(c context.Context, pid int64) (res map[string]int64, err error) {
var (
key = keyDemo(pid)
conn = d.redis.Get(c)
)
defer conn.Close()
if res, err = redis.Int64Map(conn.Do("HGETALL", key)); err != nil {
log.Error("HGETALL %v failed error(%v)", key, err)
}
return
}
```
# 扩展阅读
[memcache模块说明](cache-mc.md)

@ -1,20 +0,0 @@
# 背景
我们需要统一的cache包,用于进行各类缓存操作。
# 概览
* 缓存操作均使用连接池,保证较快的数据读写速度且提高系统的安全可靠性。
# Memcache
提供protobuf,gob,json序列化方式,gzip的memcache接口
[memcache模块说明](cache-mc.md)
# Redis
提供redis操作的各类接口以及各类将redis server返回值转换为golang类型的快捷方法。
[redis模块说明](cache-redis.md)

@ -1,118 +0,0 @@
# Paladin SDK
## 配置模块化
进行配置的模块化是为了更好地管理配置,尽可能避免由修改配置带来的失误。
在配置种类里,可以看到其实 环境配置 和 应用配置 已经由平台进行管理化。
我们通常业务里只用配置 业务配置 和 在线配置 就可以了,之前我们大部分都是单个文件配置,而为了更好管理我们需要按类型进行拆分配置文件。
例如:
| 名称 | 说明 |
|:------|:------|
| application.toml | 在线配置 |
| mysql.toml | 业务db配置 |
| hbase.toml | 业务hbase配置 |
| memcache.toml | 业务mc配置 |
| redis.toml | 业务redis配置 |
| http.toml | 业务http client/server/auth配置 |
| grpc.toml | 业务grpc client/server配置 |
## 使用方式
paladin 是一个config SDK客户端,包括了remote、file、mock几个抽象功能,方便使用本地文件或者远程配置中心,并且集成了对象自动reload功能。
### 远程配置中心
可以通过环境变量注入,例如:APP_ID/DEPLOY_ENV/ZONE/HOSTNAME,然后通过paladin实现远程配置中心SDK进行配合使用。
### 指定本地文件:
```shell
./cmd -conf=/data/conf/app/demo.toml
# or multi file
./cmd -conf=/data/conf/app/
```
### mock配置文件
```go
func TestMain(t *testing.M) {
mock := make(map[string]string])
mock["application.toml"] = `
demoSwitch = false
demoNum = 100
demoAPI = "xxx"
`
paladin.DefaultClient = paladin.NewMock(mock)
}
```
### example main
```go
// main.go
func main() {
flag.Parse()
// 初始化paladin
if err := paladin.Init(); err != nil {
panic(err)
}
log.Init(nil) // debug flag: log.dir={path}
defer log.Close()
}
```
### example HTTP/gRPC
```toml
# http.toml
[server]
addr = "0.0.0.0:9000"
timeout = "1s"
```
```go
// server.go
func NewServer() {
// 默认配置用nil,这时读取HTTP/gRPC构架中的flag或者环境变量(可能是docker注入的环境变量,默认端口:8000/9000)
engine := bm.DefaultServer(nil)
// 除非自己要替换了配置,用http.toml
var bc struct {
Server *bm.ServerConfig
}
if err := paladin.Get("http.toml").UnmarshalTOML(&bc); err != nil {
// 不存在时,将会为nil使用默认配置
if err != paladin.ErrNotExist {
panic(err)
}
}
engine := bm.DefaultServer(bc.Server)
}
```
### example Service(在线配置热加载配置)
```go
# service.go
type Service struct {
ac *paladin.Map
}
func New() *Service {
// paladin.Map 通过atomic.Value支持自动热加载
var ac = new(paladin.TOML)
if err := paladin.Watch("application.toml", ac); err != nil {
panic(err)
}
s := &Service{
ac: ac,
}
return s
}
func (s *Service) Test() {
sw, err := s.ac.Get("switch").Bool()
if err != nil {
// TODO
}
// or use default value
sw := paladin.Bool(s.ac.Get("switch"), false)
}
```

@ -1,48 +0,0 @@
# config
## 介绍
初看起来,配置管理可能很简单,但是这其实是不稳定的一个重要来源。
即变更管理导致的故障,我们目前基于配置中心(config-service)的部署方式,二进制文件的发布与配置文件的修改是异步进行的,每次变更配置,需要重新构建发布版。
由此,我们整体对配置文件进行梳理,对配置进行模块化,以及方便易用的paladin config sdk。
## 环境配置
| flag | env | remark |
|:----------|:----------|:------|
| region | REGION | 部署地区,sh-上海、gz-广州、bj-北京 |
| zone | ZONE | 分布区域,sh001-上海核心、sh004-上海嘉定 |
| deploy.env | DEPLOY_ENV | dev-开发、fat1-功能、uat-集成、pre-预发、prod-生产 |
| deploy.color | DEPLOY_COLOR | 服务颜色,blue(测试feature染色请求) |
| - | HOSTNAME | 主机名,xxx-hostname |
全局公用环境变量,通常为部署环境配置,由系统、发布系统或supervisor进行环境变量注入,并不用进行例外配置,如果是开发过程中则可以通过flag注入进行运行测试。
## 应用配置
| flag | env | default | remark |
|:----------|:----------|:-------------|:------|
| appid | APP_ID | - | 应用ID |
| http | HTTP | tcp://0.0.0.0:8000/?timeout=1s | http 监听端口 |
| http.perf | HTTP_PERF | tcp://0.0.0.0:2233/?timeout=1s | http perf 监听端口 |
| grpc | GRPC | tcp://0.0.0.0:9000/?timeout=1s&idle_timeout=60s | grpc 监听端口 |
| grpc.target | - | - | 指定服务运行:<br>-grpc.target=demo.service=127.0.0.1:9000 <br>-grpc.target=demo.service=127.0.0.2:9000 |
| discovery.nodes | DISCOVERY_NODES | - | 服务发现节点:127.0.0.1:7171,127.0.0.2:7171 |
| log.v | LOG_V | 0 | 日志级别:<br>DEBUG:0 INFO:1 WARN:2 ERROR:3 FATAL:4 |
| log.stdout | LOG_STDOUT | false | 是否标准输出:true、false|
| log.dir | LOG_DIR | - | 日志文件目录,如果配置会输出日志到文件,否则不输出日志文件 |
| log.agent | LOG_AGENT | - | 日志采集agent:<br>unixpacket:///var/run/lancer/collector_tcp.sock?timeout=100ms&chan=1024 |
| log.module | LOG_MODULE | - | 指定field信息 format: file=1,file2=2. |
| log.filter | LOG_FILTER | - | 过虑敏感信息 format: field1,field2. |
基本为一些应用相关的配置信息,通常发布系统和supervisor都有对应的部署环境进行配置注入,并不用进行例外配置,如果开发过程中可以通过flag进行注入运行测试。
## 业务配置
Redis、MySQL等业务组件,可以使用静态的配置文件来初始化,根据应用业务集群进行配置。
## 在线配置
需要在线读取、变更的配置信息,比如某个业务开关,可以实现配置reload实时更新。
## 扩展阅读
[paladin配置sdk](config-paladin.md)

@ -1,51 +0,0 @@
# database/hbase
## 说明
Hbase Client,进行封装加入了链路追踪和统计。
## 配置
需要指定hbase集群的zookeeper地址。
```
config := &hbase.Config{Zookeeper: &hbase.ZKConfig{Addrs: []string{"localhost"}}}
client := hbase.NewClient(config)
```
## 使用方式
```
package main
import (
"context"
"fmt"
"github.com/go-kratos/kratos/pkg/database/hbase"
)
func main() {
config := &hbase.Config{Zookeeper: &hbase.ZKConfig{Addrs: []string{"localhost"}}}
client := hbase.NewClient(config)
//
values := map[string]map[string][]byte{"name": {"firstname": []byte("hello"), "lastname": []byte("world")}}
ctx := context.Background()
// 写入信息
// table: user
// rowkey: user1
// values["family"] = columns
_, err := client.PutStr(ctx, "user", "user1", values)
if err != nil {
panic(err)
}
// 读取信息
// table: user
// rowkey: user1
result, err := client.GetStr(ctx, "user", "user1")
if err != nil {
panic(err)
}
fmt.Printf("%v", result)
}
```

@ -1,42 +0,0 @@
# 开始使用
## 配置
进入项目中的configs目录,mysql.toml,我们可以看到:
```toml
[demo]
addr = "127.0.0.1:3306"
dsn = "{user}:{password}@tcp(127.0.0.1:3306)/{database}?timeout=1s&readTimeout=1s&writeTimeout=1s&parseTime=true&loc=Local&charset=utf8mb4,utf8"
readDSN = ["{user}:{password}@tcp(127.0.0.2:3306)/{database}?timeout=1s&readTimeout=1s&writeTimeout=1s&parseTime=true&loc=Local&charset=utf8mb4,utf8","{user}:{password}@tcp(127.0.0.3:3306)/{database}?timeout=1s&readTimeout=1s&writeTimeout=1s&parseTime=true&loc=Local&charset=utf8,utf8mb4"]
active = 20
idle = 10
idleTimeout ="4h"
queryTimeout = "200ms"
execTimeout = "300ms"
tranTimeout = "400ms"
```
在该配置文件中我们可以配置mysql的读和写的dsn、连接地址addr、连接池的闲置连接数idle、最大连接数active以及各类超时。
如果配置了readDSN,在进行读操作的时候会优先使用readDSN的连接。
## 初始化
进入项目的internal/dao目录,打开db.go,其中:
```go
var cfg struct {
Client *sql.Config
}
checkErr(paladin.Get("db.toml").UnmarshalTOML(&dc))
```
使用paladin配置管理工具将上文中的db.toml中的配置解析为我们需要使用db的相关配置。
# TODO:补充常用方法
# 扩展阅读
[tidb模块说明](database-tidb.md)
[hbase模块说明](database-hbase.md)

@ -1,195 +0,0 @@
# 开始使用
## 配置
进入项目中的configs目录,mysql.toml,我们可以看到:
```toml
[demo]
addr = "127.0.0.1:3306"
dsn = "{user}:{password}@tcp(127.0.0.1:3306)/{database}?timeout=1s&readTimeout=1s&writeTimeout=1s&parseTime=true&loc=Local&charset=utf8mb4,utf8"
readDSN = ["{user}:{password}@tcp(127.0.0.2:3306)/{database}?timeout=1s&readTimeout=1s&writeTimeout=1s&parseTime=true&loc=Local&charset=utf8mb4,utf8","{user}:{password}@tcp(127.0.0.3:3306)/{database}?timeout=1s&readTimeout=1s&writeTimeout=1s&parseTime=true&loc=Local&charset=utf8,utf8mb4"]
active = 20
idle = 10
idleTimeout ="4h"
queryTimeout = "200ms"
execTimeout = "300ms"
tranTimeout = "400ms"
```
在该配置文件中我们可以配置mysql的读和写的dsn、连接地址addr、连接池的闲置连接数idle、最大连接数active以及各类超时。
如果配置了readDSN,在进行读操作的时候会优先使用readDSN的连接。
## 初始化
进入项目的internal/dao目录,打开db.go,其中:
```go
var cfg struct {
Client *sql.Config
}
checkErr(paladin.Get("db.toml").UnmarshalTOML(&dc))
```
使用paladin配置管理工具将上文中的db.toml中的配置解析为我们需要使用db的相关配置。
```go
// Dao dao.
type Dao struct {
db *sql.DB
}
```
在dao的主结构提中定义了mysql的连接池对象。
```go
d = &dao{
db: sql.NewMySQL(dc.Demo),
}
```
使用kratos/pkg/database/sql包的NewMySQL方法进行连接池对象的初始化,需要传入上文解析的配置。
## Ping
```go
// Ping ping the resource.
func (d *dao) Ping(ctx context.Context) (err error) {
return d.db.Ping(ctx)
}
```
生成的dao层模板中自带了mysql相关的ping方法,用于为负载均衡服务的健康监测提供依据,详见[blademaster](blademaster-quickstart.md)。
## 关闭
```go
// Close close the resource.
func (d *dao) Close() {
d.db.Close()
}
```
在关闭dao层时,通过调用mysql连接池对象的Close方法,我们可以关闭该连接池,从而释放相关资源。
# 常用方法
## 单个查询
```go
// GetDemo 用户角色
func (d *dao) GetDemo(c context.Context, did int64) (demo int8, err error) {
err = d.db.QueryRow(c, _getDemoSQL, did).Scan(&demo)
if err != nil && err != sql.ErrNoRows {
log.Error("d.GetDemo.Query error(%v)", err)
return
}
return demo, nil
}
```
db.QueryRow方法用于返回最多一条记录的查询,在QueryRow方法后使用Scan方法即可将mysql的返回值转换为Golang的数据类型。
当mysql查询不到对应数据时,会返回sql.ErrNoRows,如果不需处理,可以参考如上代码忽略此error。
## 批量查询
```go
// ResourceLogs ResourceLogs.
func (d *dao) GetDemos(c context.Context, dids []int64) (demos []int8, err error) {
rows, err := d.db.Query(c, _getDemosSQL, dids)
if err != nil {
log.Error("query error(%v)", err)
return
}
defer rows.Close()
for rows.Next() {
var tmpD int8
if err = rows.Scan(&tmpD); err != nil {
log.Error("scan demo log error(%v)", err)
return
}
demos = append(demos, tmpD)
}
return
}
```
db.Query方法一般用于批量查询的场景,返回*sql.Rows和error信息。
我们可以使用rows.Next()方法获得下一行的返回结果,并且配合使用rows.Scan()方法将该结果转换为Golang的数据类型。当没有下一行时,rows.Next方法将返回false,此时循环结束。
注意,在使用完毕rows对象后,需要调用rows.Close方法关闭连接,释放相关资源。
## 执行语句
```go
// DemoExec exec
func (d *Dao) DemoExec(c context.Context, id int64) (rows int64, err error) {
res, err := d.db.Exec(c, _demoUpdateSQL, id)
if err != nil {
log.Error("db.DemoExec.Exec(%s) error(%v)", _demoUpdateSQL, err)
return
}
return res.RowsAffected()
}
```
执行UPDATE/DELETE/INSERT语句时,使用db.Exec方法进行语句执行,返回*sql.Result和error信息:
```go
// A Result summarizes an executed SQL command.
type Result interface {
LastInsertId() (int64, error)
RowsAffected() (int64, error)
}
```
Result接口支持获取影响行数和LastInsertId(一般用于获取Insert语句插入数据库后的主键ID)
## 事务
kratos/pkg/database/sql包支持事务操作,具体操作示例如下:
开启一个事务:
```go
tx := d.db.Begin()
if err = tx.Error; err != nil {
log.Error("db begin transcation failed, err=%+v", err)
return
}
```
在事务中执行语句:
```go
res, err := tx.Exec(_demoSQL, did)
if err != nil {
return
}
rows := res.RowsAffected()
```
提交事务:
```go
if err = tx.Commit().Error; err!=nil{
log.Error("db commit transcation failed, err=%+v", err)
}
```
回滚事务:
```go
if err = tx.Rollback().Error; err!=nil{
log.Error("db rollback failed, err=%+v", rollbackErr)
}
```
# 扩展阅读
- [tidb模块说明](database-tidb.md)
- [hbase模块说明](database-hbase.md)

@ -1,19 +0,0 @@
# database/sql
## 背景
数据库驱动,进行封装加入了熔断、链路追踪和统计,以及链路超时。
通常数据模块都写在`internal/dao`目录中,并提供对应的数据访问接口。
## MySQL
MySQL数据库驱动,支持读写分离、context、timeout、trace和统计功能,以及错误熔断防止数据库雪崩。
[mysql client](database-mysql.md)
[mysql client orm](database-mysql-orm.md)
## HBase
HBase客户端,支持trace、slowlog和统计功能。
[hbase client](database-hbase.md)
## TiDB
TiDB客户端,支持服务发现和熔断功能。
[tidb client](database-tidb.md)

@ -0,0 +1,14 @@
# Kratos v2 Kit Design
MaoJian
Last updated: December 25, 2020
## Abstract
kratos v1 基础库主要专注在各类功能的细节实现,比如 gRPC 的负载均衡,熔断器等一系列微服务需要的功能。
## Background
## Proposal
## Implementation

@ -1,102 +0,0 @@
# ecode
## 背景
错误码一般被用来进行异常传递,且需要具有携带`message`文案信息的能力。
## 错误码之Codes
在`kratos`里,错误码被设计成`Codes`接口,声明如下[代码位置](https://github.com/go-kratos/kratos/blob/master/pkg/ecode/ecode.go):
```go
// Codes ecode error interface which has a code & message.
type Codes interface {
// sometimes Error return Code in string form
// NOTE: don't use Error in monitor report even it also work for now
Error() string
// Code get error code.
Code() int
// Message get code message.
Message() string
//Detail get error detail,it may be nil.
Details() []interface{}
}
// A Code is an int error code spec.
type Code int
```
可以看到该接口一共有四个方法,且`type Code int`结构体实现了该接口。
### 注册message
一个`Code`错误码可以对应一个`message`,默认实现会从全局变量`_messages`中获取,业务可以将自定义`Code`对应的`message`通过调用`Register`方法的方式传递进去,如:
```go
cms := map[int]string{
0: "很好很强大!",
-304: "啥都没变啊~",
-404: "啥都没有啊~",
}
ecode.Register(cms)
fmt.Println(ecode.OK.Message()) // 输出:很好很强大!
```
注意:`map[int]string`类型并不是绝对,比如有业务要支持多语言的场景就可以扩展为类似`map[int]LangStruct`的结构,因为全局变量`_messages`是`atomic.Value`类型,只需要修改对应的`Message`方法实现即可。
### Details
`Details`接口为`gRPC`预留,`gRPC`传递异常会将服务端的错误码pb序列化之后赋值给`Details`,客户端拿到之后反序列化得到,具体可阅读`status`的实现:
1. `ecode`包内的`Status`结构体实现了`Codes`接口[代码位置](https://github.com/go-kratos/kratos/blob/master/pkg/ecode/status.go)
2. `warden/internal/status`包内包装了`ecode.Status`和`grpc.Status`进行互相转换的方法[代码位置](https://github.com/go-kratos/kratos/blob/master/pkg/net/rpc/warden/internal/status/status.go)
3. `warden`的`client`和`server`则使用转换方法将`gRPC`底层返回的`error`最终转换为`ecode.Status` [代码位置](https://github.com/go-kratos/kratos/blob/master/pkg/net/rpc/warden/client.go#L162)
## 转换为ecode
错误码转换有以下两种情况:
1. 因为框架传递错误是靠`ecode`错误码,比如bm框架返回的`code`字段默认就是数字,那么客户端接收到如`{"code":-404}`的话,可以使用`ec := ecode.Int(-404)`或`ec := ecode.String("-404")`来进行转换。
2. 在项目中`dao`层返回一个错误码,往往返回参数类型建议为`error`而不是`ecode.Codes`,因为`error`更通用,那么上层`service`就可以使用`ec := ecode.Cause(err)`进行转换。
## 判断
错误码判断是否相等:
1. `ecode`与`ecode`判断使用:`ecode.Equal(ec1, ec2)`
2. `ecode`与`error`判断使用:`ecode.EqualError(ec, err)`
## 使用工具生成
使用proto协议定义错误码,格式如下:
```proto
// user.proto
syntax = "proto3";
package ecode;
enum UserErrCode {
UserUndefined = 0; // 因protobuf协议限制必须存在!!!无意义的0,工具生成代码时会忽略该参数
UserNotLogin = 123; // 正式错误码
}
```
需要注意以下几点:
1. 必须是enum类型,且名字规范必须以"ErrCode"结尾,如:UserErrCode
2. 因为protobuf协议限制,第一个enum值必须为无意义的0
使用`kratos tool protoc --ecode user.proto`进行生成,生成如下代码:
```go
package ecode
import (
"github.com/go-kratos/kratos/pkg/ecode"
)
var _ ecode.Codes
// UserErrCode
var (
UserNotLogin = ecode.New(123);
)
```

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Before

Width:  |  Height:  |  Size: 5.8 KiB

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 661 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 64 KiB

@ -1,30 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Kratos Documentation</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<meta name="description" content="Description">
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<link rel="stylesheet" href="//cdn.staticfile.org/docsify/4.11.6/themes/vue.min.css">
</head>
<body>
<div id="app"></div>
<script src="//cdn.staticfile.org/docsify/4.11.6/docsify.min.js"></script>
<script src="//cdn.staticfile.org/docsify/4.11.6/plugins/search.min.js"></script>
<script src="//cdn.jsdelivr.net/npm/docsify-edit-on-github"></script>
<script>
window.$docsify = {
loadSidebar: true,
auto2top: true,
autoHeader: true,
name: 'go-kratos/kratos',
repo: 'https://github.com/go-kratos/kratos',
search: 'auto',
plugins: [
EditOnGithubPlugin.create('https://github.com/go-kratos/kratos/blob/master/docs/')
]
}
</script>
</body>
</html>

@ -1,66 +0,0 @@
# 安装
1.安装protoc二进制文件
```
下载地址:https://github.com/google/protobuf/releases
mv bin/protoc /usr/local/bin/
mv -r include/google /usr/local/include/
```
2.安装protobuf库文件
```
go get -u github.com/golang/protobuf/proto
```
3.安装goprotobuf插件
```
go get github.com/golang/protobuf/protoc-gen-go
```
4.安装gogoprotobuf插件和依赖
```
//gogo
go get github.com/gogo/protobuf/protoc-gen-gogo
//gofast
go get github.com/gogo/protobuf/protoc-gen-gofast
//依赖
go get github.com/gogo/protobuf/proto
go get github.com/gogo/protobuf/gogoproto
```
5.安装框架依赖
```
# grpc (或者git clone https://github.com/grpc/grpc-go 然后复制到google.golang.org/grpc)
go get -u google.golang.org/grpc
# genproto (或者git clone https://github.com/google/go-genproto 然后复制到google.golang.org/genproto)
go get google.golang.org/genproto/...
```
6.安装kratos tool
```
go get -u github.com/go-kratos/kratos/tool/kratos
cd $GOPATH/src
kratos new kratos-demo --proto
```
7.运行
```
cd kratos-demo/cmd
go build
./cmd -conf ../configs
```
打开浏览器访问:[http://localhost:8000/kratos-demo/start](http://localhost:8000/kratos-demo/start),你会看到输出了`Golang 大法好 !!!`
[kratos工具](kratos-tool.md)

@ -1,27 +0,0 @@
### kratos tool genbts
> 缓存回源代码生成
在internal/dao/dao.go中添加mc缓存interface定义,可以指定对应的[注解参数](../../tool/kratos-gen-bts/README.md);
并且在接口前面添加`go:generate kratos tool genbts`;
然后在当前目录执行`go generate`,可以看到自动生成的dao.bts.go代码。
### 回源模板
```go
//go:generate kratos tool genbts
type _bts interface {
// bts: -batch=2 -max_group=20 -batch_err=break -nullcache=&Demo{ID:-1} -check_null_code=$.ID==-1
Demos(c context.Context, keys []int64) (map[int64]*Demo, error)
// bts: -sync=true -nullcache=&Demo{ID:-1} -check_null_code=$.ID==-1
Demo(c context.Context, key int64) (*Demo, error)
// bts: -paging=true
Demo1(c context.Context, key int64, pn int, ps int) (*Demo, error)
// bts: -nullcache=&Demo{ID:-1} -check_null_code=$.ID==-1
None(c context.Context) (*Demo, error)
}
```
### 参考
也可以参考完整的testdata例子:kratos/tool/kratos-gen-bts/testdata

@ -1,68 +0,0 @@
### kratos tool genmc
> 缓存代码生成
在internal/dao/dao.go中添加mc缓存interface定义,可以指定对应的[注解参数](../../tool/kratos-gen-mc/README.md);
并且在接口前面添加`go:generate kratos tool genmc`;
然后在当前目录执行`go generate`,可以看到自动生成的mc.cache.go代码。
### 缓存模板
```go
//go:generate kratos tool genmc
type _mc interface {
// mc: -key=demoKey
CacheDemos(c context.Context, keys []int64) (map[int64]*Demo, error)
// mc: -key=demoKey
CacheDemo(c context.Context, key int64) (*Demo, error)
// mc: -key=keyMid
CacheDemo1(c context.Context, key int64, mid int64) (*Demo, error)
// mc: -key=noneKey
CacheNone(c context.Context) (*Demo, error)
// mc: -key=demoKey
CacheString(c context.Context, key int64) (string, error)
// mc: -key=demoKey -expire=d.demoExpire -encode=json
AddCacheDemos(c context.Context, values map[int64]*Demo) error
// mc: -key=demo2Key -expire=d.demoExpire -encode=json
AddCacheDemos2(c context.Context, values map[int64]*Demo, tp int64) error
// 这里也支持自定义注释 会替换默认的注释
// mc: -key=demoKey -expire=d.demoExpire -encode=json|gzip
AddCacheDemo(c context.Context, key int64, value *Demo) error
// mc: -key=keyMid -expire=d.demoExpire -encode=gob
AddCacheDemo1(c context.Context, key int64, value *Demo, mid int64) error
// mc: -key=noneKey
AddCacheNone(c context.Context, value *Demo) error
// mc: -key=demoKey -expire=d.demoExpire
AddCacheString(c context.Context, key int64, value string) error
// mc: -key=demoKey
DelCacheDemos(c context.Context, keys []int64) error
// mc: -key=demoKey
DelCacheDemo(c context.Context, key int64) error
// mc: -key=keyMid
DelCacheDemo1(c context.Context, key int64, mid int64) error
// mc: -key=noneKey
DelCacheNone(c context.Context) error
}
func demoKey(id int64) string {
return fmt.Sprintf("art_%d", id)
}
func demo2Key(id, tp int64) string {
return fmt.Sprintf("art_%d_%d", id, tp)
}
func keyMid(id, mid int64) string {
return fmt.Sprintf("art_%d_%d", id, mid)
}
func noneKey() string {
return "none"
}
```
### 参考
也可以参考完整的testdata例子:kratos/tool/kratos-gen-mc/testdata

@ -1,28 +0,0 @@
### kratos tool protoc
```shell
# generate all
kratos tool protoc api.proto
# generate gRPC
kratos tool protoc --grpc api.proto
# generate BM HTTP
kratos tool protoc --bm api.proto
# generate ecode
kratos tool protoc --ecode api.proto
# generate swagger
kratos tool protoc --swagger api.proto
```
执行生成如 `api.pb.go/api.bm.go/api.swagger.json/api.ecode.go` 的对应文件,需要注意的是:`ecode`生成有固定规则,需要首先是`enum`类型,且`enum`名字要以`ErrCode`结尾,如`enum UserErrCode`。详情可见:[example](https://github.com/go-kratos/kratos/tree/master/example/protobuf)
> 该工具在Windows/Linux下运行,需提前安装好 [protobuf](https://github.com/google/protobuf) 工具
`kratos tool protoc`本质上是拼接好了`protoc`命令然后进行执行,在执行时会打印出对应执行的`protoc`命令,如下可见:
```shell
protoc --proto_path=$GOPATH --proto_path=$GOPATH/github.com/go-kratos/kratos/third_party --proto_path=. --bm_out=:. api.proto
protoc --proto_path=$GOPATH --proto_path=$GOPATH/github.com/go-kratos/kratos/third_party --proto_path=. --gofast_out=plugins=grpc:. api.proto
protoc --proto_path=$GOPATH --proto_path=$GOPATH/github.com/go-kratos/kratos/third_party --proto_path=. --bswagger_out=:. api.proto
protoc --proto_path=$GOPATH --proto_path=$GOPATH/github.com/go-kratos/kratos/third_party --proto_path=. --ecode_out=:. api.proto
```

@ -1,8 +0,0 @@
### kratos tool swagger
```shell
kratos tool swagger serve api/api.swagger.json
```
执行命令后,浏览器会自动打开swagger文档地址。
同时也可以查看更多的 [go-swagger](https://github.com/go-swagger/go-swagger) 官方参数进行使用。

@ -1,106 +0,0 @@
# 介绍
kratos包含了一批好用的工具集,比如项目一键生成、基于proto生成http&grpc代码,生成缓存回源代码,生成memcache执行代码,生成swagger文档等。
# 获取工具
执行以下命令,即可快速安装好`kratos`工具
```shell
go get -u github.com/go-kratos/kratos/tool/kratos
```
那么接下来让我们快速开始熟悉工具的用法~
# kratos本体
`kratos`是所有工具集的本体,就像`go`一样,拥有执行各种子工具的能力,如`go build`和`go tool`。先让我们看看`-h`的输出:
```
NAME:
kratos - kratos tool
USAGE:
kratos [global options] command [command options] [arguments...]
VERSION:
0.0.1
COMMANDS:
new, n create new project
build, b kratos build
run, r kratos run
tool, t kratos tool
version, v kratos version
self-upgrade kratos self-upgrade
help, h Shows a list of commands or help for one command
GLOBAL OPTIONS:
--help, -h show help
--version, -v print the version
```
可以看到`kratos`有如:`new` `build` `run` `tool`等在内的COMMANDS,那么接下来一一演示如何使用。
# kratos new
`kratos new`是快速创建一个项目的命令,执行如下:
```shell
kratos new kratos-demo
```
即可快速在当前目录生成一个叫`kratos-demo`的项目。此外还支持指定owner和path,如下:
```shell
kratos new kratos-demo -o YourName -d YourPath
```
注意,`kratos new`默认会生成通过 protobuf 定义的`grpc`和`bm`示例代码的,如只生成bm请加`--http`,如下:
```shell
kratos new kratos-demo -o YourName -d YourPath --http
```
如只生成grpc请加`--grpc`,如下:
```shell
kratos new kratos-demo -o YourName -d YourPath --grpc
```
> 特别注意,如果不是MacOS系统,需要自己进行手动安装protoc,用于生成的示例项目`api`目录下的`proto`文件并不会自动生成对应的`.pb.go`和`.bm.go`文件。
> 也可以参考以下说明进行生成:[protoc说明](protoc.md)
# kratos build & run
`kratos build`和`kratos run`是`go build`和`go run`的封装,可以在当前项目任意目录进行快速运行进行调试,并无特别用途。
# kratos tool
`kratos tool`是基于proto生成http&grpc代码,生成缓存回源代码,生成memcache执行代码,生成swagger文档等工具集,先看下的执行效果:
```
kratos tool
protoc(已安装): 快速方便生成pb.go的protoc封装,windows、Linux请先安装protoc工具 Author(kratos) [2019/10/31]
genbts(已安装): 缓存回源逻辑代码生成器 Author(kratos) [2019/10/31]
testcli(已安装): 测试代码生成 Author(kratos) [2019/09/09]
genmc(已安装): mc缓存代码生成 Author(kratos) [2019/07/23]
swagger(已安装): swagger api文档 Author(goswagger.io) [2019/05/05]
安装工具: kratos tool install demo
执行工具: kratos tool demo
安装全部工具: kratos tool install all
全部升级: kratos tool upgrade all
```
> 小小说明:如未安装工具,第一次运行也可自动安装,不需要特别执行install
目前已经集成的工具有:
* [kratos](kratos-tool.md) 为本体工具,只用于安装更新使用;
* [protoc](kratos-protoc.md) 用于快速生成gRPC、HTTP、Swagger文件,该命令Windows,Linux用户需要手动安装 protobuf 工具;
* [swagger](kratos-swagger.md) 用于显示自动生成的HTTP API接口文档,通过 `kratos tool swagger serve api/api.swagger.json` 可以查看文档;
* [genmc](kratos-genmc.md) 用于自动生成memcached缓存代码;
* [genbts](kratos-genbts.md) 用于生成缓存回源代码生成,如果miss则调用回源函数从数据源获取,然后塞入缓存;

@ -1,34 +0,0 @@
# 日志基础库
## 概览
基于[zap](https://github.com/uber-go/zap)的field方式实现的高性能log库,提供Info、Warn、Error日志级别;
并提供了context支持,方便打印环境信息以及日志的链路追踪,在框架中都通过field方式实现,避免format日志带来的性能消耗。
## 配置选项
| flag | env | type | remark |
|:----------|:----------|:-------------:|:------|
| log.v | LOG_V | int | 日志级别:DEBUG:0 INFO:1 WARN:2 ERROR:3 FATAL:4 |
| log.stdout | LOG_STDOUT | bool | 是否标准输出:true、false|
| log.dir | LOG_DIR | string | 日志文件目录,如果配置会输出日志到文件,否则不输出日志文件 |
| log.agent | LOG_AGENT | string | 日志采集agent:unixpacket:///var/run/lancer/collector_tcp.sock?timeout=100ms&chan=1024 |
| log.module | LOG_MODULE | string | 指定field信息 format: file=1,file2=2. |
| log.filter | LOG_FILTER | string | 过虑敏感信息 format: field1,field2. |
## 使用方式
```go
func main() {
// 解析flag
flag.Parse()
// 初始化日志模块
log.Init(nil)
// 打印日志
log.Info("hi:%s", "kratos")
log.Infoc(Context.TODO(), "hi:%s", "kratos")
log.Infov(Context.TODO(), log.KVInt("key1", 100), log.KVString("key2", "test value")
}
```
## 扩展阅读
* [log-agent](log-agent.md)

@ -1,26 +0,0 @@
# protoc
`protobuf`是Google官方出品的一种轻便高效的结构化数据存储格式,可以用于结构化数据串行化,或者说序列化。它很适合做数据存储或 RPC 数据交换格式。可用于通讯协议、数据存储等领域的语言无关、平台无关、可扩展的序列化结构数据格式。
使用`protobuf`,需要先书写`.proto`文件,然后编译该文件。编译`proto`文件则需要使用到官方的`protoc`工具,安装文档请参看:[google官方protoc工具](https://github.com/protocolbuffers/protobuf#protocol-compiler-installation)。
注意:`protoc`是用于编辑`proto`文件的工具,它并不具备生成对应语言代码的能力,所以正常都是`protoc`配合对应语言的代码生成工具来使用,如Go语言的[gogo protobuf](https://github.com/gogo/protobuf),请先点击按文档说明安装。
安装好对应工具后,我们可以进入`api`目录,执行如下命令:
```shell
export $KRATOS_HOME = kratos路径
export $KRATOS_DEMO = 项目路径
// 生成:api.pb.go
protoc -I$GOPATH/src:$KRATOS_HOME/tool/protobuf/pkg/extensions:$KRATOS_DEMO/api --gogofast_out=plugins=grpc:$KRATOS_DEMO/api $KRATOS_DEMO/api/api.proto
// 生成:api.bm.go
protoc -I$GOPATH/src:$KRATOS_HOME/tool/protobuf/pkg/extensions:$KRATOS_DEMO/api --bm_out=$KRATOS_DEMO/api $KRATOS_DEMO/api/api.proto
// 生成:api.swagger.json
protoc -I$GOPATH/src:$KRATOS_HOME/tool/protobuf/pkg/extensions:$KRATOS_DEMO/api --bswagger_out=$KRATOS_DEMO/api $KRATOS_DEMO/api/api.proto
```
请注意替换`/Users/felix/work/go/src`目录为你本地开发环境对应GOPATH目录,其中`--gogofast_out`意味着告诉`protoc`工具需要使用`gogo protobuf`的工具生成代码。

@ -1,71 +0,0 @@
# 快速开始
**在安装之前,请确认您的开发环境中正确安装了[golang](https://golang.org/), [git](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git), 和[protoc](https://grpc.io/docs/protoc-installation/)**
创建kratos项目,可以使用`kratos`工具,如下:
```shell
go get -u github.com/go-kratos/kratos/tool/kratos
cd $GOPATH/src
kratos new kratos-demo
```
根据提示可以快速创建项目,如[kratos-demo](https://github.com/go-kratos/kratos-demo)就是通过工具创建生成。目录结构如下:
```
├── CHANGELOG.md
├── OWNERS
├── README.md
├── api # api目录为对外保留的proto文件及生成的pb.go文件
   ├── api.bm.go
   ├── api.pb.go # 通过go generate生成的pb.go文件
   ├── api.proto
   └── client.go
├── cmd
   └── main.go # cmd目录为main所在
├── configs # configs为配置文件目录
   ├── application.toml # 应用的自定义配置文件,可能是一些业务开关如:useABtest = true
   ├── db.toml # db相关配置
   ├── grpc.toml # grpc相关配置
   ├── http.toml # http相关配置
   ├── memcache.toml # memcache相关配置
   └── redis.toml # redis相关配置
├── go.mod
├── go.sum
└── internal # internal为项目内部包,包括以下目录:
   ├── dao # dao层,用于数据库、cache、MQ、依赖某业务grpc|http等资源访问
     ├── dao.bts.go
     ├── dao.go
     ├── db.go
     ├── mc.cache.go
     ├── mc.go
     └── redis.go
   ├── di # 依赖注入层 采用wire静态分析依赖
     ├── app.go
     ├── wire.go # wire 声明
     └── wire_gen.go # go generate 生成的代码
   ├── model # model层,用于声明业务结构体
     └── model.go
   ├── server # server层,用于初始化grpc和http server
     ├── grpc # grpc层,用于初始化grpc server和定义method
       └── server.go
     └── http # http层,用于初始化http server和声明handler
     └── server.go
   └── service # service层,用于业务逻辑处理,且为方便http和grpc共用方法,建议入参和出参保持grpc风格,且使用pb文件生成代码
   └── service.go
└── test # 测试资源层 用于存放测试相关资源数据 如docker-compose配置 数据库初始化语句等
└── docker-compose.yaml
```
生成后可直接运行如下:
```shell
cd kratos-demo/cmd
go build
./cmd -conf ../configs
```
打开浏览器访问:[http://localhost:8000/kratos-demo/start](http://localhost:8000/kratos-demo/start),你会看到输出了`Golang 大法好 !!!`
[kratos工具](kratos-tool.md)

@ -1,57 +0,0 @@
# 自适应限流保护
kratos 借鉴了 Sentinel 项目的自适应限流系统,通过综合分析服务的 cpu 使用率、请求成功的 qps 和请求成功的 rt 来做自适应限流保护。
## 核心目标
* 自动嗅探负载和 qps,减少人工配置
* 削顶,保证超载时系统不被拖垮,并能以高水位 qps 继续运行
## 限流规则
### 指标介绍
| 指标名称 | 指标含义 |
| -------- | ------------------------------------------------------------- |
| cpu | 最近 1s 的 CPU 使用率均值,使用滑动平均计算,采样周期是 250ms |
| inflight | 当前处理中正在处理的请求数量 |
| pass | 请求处理成功的量 |
| rt | 请求成功的响应耗时 |
### 滑动窗口
在自适应限流保护中,采集到的指标的时效性非常强,系统只需要采集最近一小段时间内的 qps、rt 即可,对于较老的数据,会自动丢弃。为了实现这个效果,kratos 使用了滑动窗口来保存采样数据。
![ratelimit-rolling-window](img/ratelimit-rolling-window.png)
如上图,展示了一个具有两个桶(bucket)的滑动窗口(rolling window)。整个滑动窗口用来保存最近 1s 的采样数据,每个小的桶用来保存 500ms 的采样数据。
当时间流动之后,过期的桶会自动被新桶的数据覆盖掉,在图中,在 1000-1500ms 时,bucket 1 的数据因为过期而被丢弃,之后 bucket 3 的数据填到了窗口的头部。
### 限流公式
判断是否丢弃当前请求的算法如下:
`cpu > 800 AND (Now - PrevDrop) < 1s AND (MaxPass * MinRt * windows / 1000) < InFlight`
MaxPass 表示最近 5s 内,单个采样窗口中最大的请求数。
MinRt 表示最近 5s 内,单个采样窗口中最小的响应时间。
windows 表示一秒内采样窗口的数量,默认配置中是 5s 50 个采样,那么 windows 的值为 10。
## 压测报告
场景1,请求以每秒增加1个的速度不停上升,压测效果如下:
![ratelimit-benchmark-up-1](img/ratelimit-benchmark-up-1.png)
左测是没有限流的压测效果,右侧是带限流的压测效果。
可以看到,没有限流的场景里,系统在 700qps 时开始抖动,在 1k qps 时被拖垮,几乎没有新的请求能被放行,然而在使用限流之后,系统请求能够稳定在 600 qps 左右,rt 没有暴增,服务也没有被打垮,可见,限流有效的保护了服务。
## 参考资料
[Sentinel 系统自适应限流](https://github.com/alibaba/Sentinel/wiki/%E7%B3%BB%E7%BB%9F%E8%87%AA%E9%80%82%E5%BA%94%E9%99%90%E6%B5%81)

@ -1,42 +0,0 @@
# 背景
当代的互联网的服务,通常都是用复杂的、大规模分布式集群来实现的。互联网应用构建在不同的软件模块集上,这些软件模块,有可能是由不同的团队开发、可能使用不同的编程语言来实现、有可能布在了几千台服务器,横跨多个不同的数据中心。因此,就需要一些可以帮助理解系统行为、用于分析性能问题的工具。
# 概览
* kratos内部的trace基于opentracing语义
* 使用protobuf协议描述trace结构
* 全链路支持(gRPC/HTTP/MySQL/Redis/Memcached等)
## 参考文档
[opentracing](https://github.com/opentracing-contrib/opentracing-specification-zh/blob/master/specification.md)
[dapper](https://bigbully.github.io/Dapper-translation/)
# 使用
kratos本身不提供整套`trace`数据方案,但在`net/trace/report.go`内声明了`repoter`接口,可以简单的集成现有开源系统,比如:`zipkin`和`jaeger`。
### zipkin使用
可以看[zipkin](https://github.com/go-kratos/kratos/tree/master/pkg/net/trace/zipkin)的协议上报实现,具体使用方式如下:
1. 前提是需要有一套自己搭建的`zipkin`集群
2. 在业务代码的`main`函数内进行初始化,代码如下:
```go
// 忽略其他代码
import "github.com/go-kratos/kratos/pkg/net/trace/zipkin"
// 忽略其他代码
func main(){
// 忽略其他代码
zipkin.Init(&zipkin.Config{
Endpoint: "http://localhost:9411/api/v2/spans",
})
// 忽略其他代码
}
```
### zipkin效果图
![zipkin](img/zipkin.jpg)

@ -1,500 +0,0 @@
## 单元测试辅助工具
在单元测试中,我们希望每个测试用例都是独立的。这时候就需要Stub, Mock, Fakes等工具来帮助我们进行用例和依赖之间的隔离。
同时通过对错误情况的 Mock 也可以帮我们检查代码多个分支结果,从而提高覆盖率。
以下工具已加入到 Kratos 框架 go modules,可以借助 testgen 代码生成器自动生成部分工具代码,请放心食用。更多使用方法还欢迎大家多多探索。
### GoConvey
GoConvey是一套针对golang语言的BDD类型的测试框架。提供了良好的管理和执行测试用例的方式,包含丰富的断言函数,而且同时有测试执行和报告Web界面的支持。
#### 使用特性
为了更好的使用 GoConvey 来编写和组织测试用例,需要注意以下几点特性:
1. Convey方法和So方法的使用
> - Convey方法声明了一种规格的组织,每个组织内包含一句描述和一个方法。在方法内也可以嵌套其他Convey语句和So语句。
```Go
// 顶层Convey方法,需引入*testing.T对象
Convey(description string, t *testing.T, action func())
// 其他嵌套Convey方法,无需引入*testing.T对象
Convey(description string, action func())
```
注:同一Scope下的Convey语句描述不可以相同!
> - So方法是断言方法,用于对执行结果进行比对。GoConvey官方提供了大量断言,同时也可以自定义自己的断言([戳这里了解官方文档](https://github.com/smartystreets/goconvey/wiki/Assertions))
```Go
// A=B断言
So(A, ShouldEqual, B)
// A不为空断言
So(A, ShouldNotBeNil)
```
2. 执行次序
> 假设有以下Convey伪代码,执行次序将为A1B2A1C3。将Convey方法类比树的结点的话,整体执行类似树的遍历操作。
> 所以Convey A部分可在组织测试用例时,充当“Setup”的方法。用于初始化等一些操作。
```Go
Convey伪代码
Convey A
So 1
Convey B
So 2
Convey C
So 3
```
3. Reset方法
> GoConvey提供了Reset方法来进行“Teardown”的操作。用于执行完测试用例后一些状态的回收,连接关闭等操作。Reset方法不可与顶层Convey语句在同层。
```Go
// Reset
Reset func(action func())
```
假设有以下带有Reset方法的伪代码,同层Convey语句执行完后均会执行同层的Reset方法。执行次序为A1B2C3EA1D4E。
```Go
Convey A
So 1
Convey B
So 2
Convey C
So 3
Convey D
So 4
Reset E
```
4. 自然语言逻辑到测试用例的转换
> 在了解了Convey方法的特性和执行次序后,我们可以通过这些性质把对一个方法的测试用例按照日常逻辑组织起来。尤其建议使用Given-When-Then的形式来组织
> - 比较直观的组织示例
```Go
Convey("Top-level", t, func() {
// Setup 工作,在本层内每个Convey方法执行前都会执行的部分:
db.Open()
db.Initialize()
Convey("Test a query", func() {
db.Query()
// TODO: assertions here
})
Convey("Test inserts", func() {
db.Insert()
// TODO: assertions here
})
Reset(func() {
// Teardown工作,在本层内每个Convey方法执行完后都会执行的部分:
db.Close()
})
})
```
> - 定义单独的包含Setup和Teardown的帮助方法
```Go
package main
import (
"database/sql"
"testing"
_ "github.com/lib/pq"
. "github.com/smartystreets/goconvey/convey"
)
// 帮助方法,将原先所需的处理方法以参数(f)形式传入
func WithTransaction(db *sql.DB, f func(tx *sql.Tx)) func() {
return func() {
// Setup工作
tx, err := db.Begin()
So(err, ShouldBeNil)
Reset(func() {
// Teardown工作
/* Verify that the transaction is alive by executing a command */
_, err := tx.Exec("SELECT 1")
So(err, ShouldBeNil)
tx.Rollback()
})
// 调用传入的闭包做实际的事务处理
f(tx)
}
}
func TestUsers(t *testing.T) {
db, err := sql.Open("postgres", "postgres://localhost?sslmode=disable")
if err != nil {
panic(err)
}
Convey("Given a user in the database", t, WithTransaction(db, func(tx *sql.Tx) {
_, err := tx.Exec(`INSERT INTO "Users" ("id", "name") VALUES (1, 'Test User')`)
So(err, ShouldBeNil)
Convey("Attempting to retrieve the user should return the user", func() {
var name string
data := tx.QueryRow(`SELECT "name" FROM "Users" WHERE "id" = 1`)
err = data.Scan(&name)
So(err, ShouldBeNil)
So(name, ShouldEqual, "Test User")
})
}))
}
```
#### 使用建议
强烈建议使用 [testgen](ut-testgen.md) 进行测试用例的生成,生成后每个方法将包含一个符合以下规范的正向用例。
用例规范:
1. 每个方法至少包含一个测试方法(命名为Test[PackageName][FunctionName])
2. 每个测试方法包含一个顶层Convey语句,仅在此引入admin *testing.T类型的对象,在该层进行变量声明。
3. 每个测试方法不同的用例用Convey方法组织
4. 每个测试用例的一组断言用一个Convey方法组织
5. 使用convey.C保持上下文一致
### MonkeyPatching
#### 特性和使用条件
1. Patch()对任何无接收者的方法均有效
2. PatchInstanceMethod()对有接收者的包内/私有方法无法工作(因使用到了反射机制)。可以采用给私有方法的下一级打补丁,或改为无接收者的方法,或将方法转为公有
#### 适用场景(建议)
项目代码中上层对下层包依赖时,下层包方法Mock(例如service层对dao层方法依赖时)
基础库(MySql, Memcache, Redis)错误Mock
其他标准库,基础库以及第三方包方法Mock
#### 使用示例
1. 上层包对下层包依赖示例
Service层对Dao层依赖:
```GO
// 原方法
func (s *Service) realnameAlipayApply(c context.Context, mid int64) (info *model.RealnameAlipayApply, err error) {
if info, err = s.mbDao.RealnameAlipayApply(c, mid); err != nil {
return
}
...
return
}
// 测试方法
func TestServicerealnameAlipayApply(t *testing.T) {
convey.Convey("realnameAlipayApply", t, func(ctx convey.C) {
...
ctx.Convey("When everything goes positive", func(ctx convey.C) {
guard := monkey.PatchInstanceMethod(reflect.TypeOf(s.mbDao), "RealnameAlipayApply", func(_ *dao.Dao, _ context.Context, _ int64) (*model.RealnameAlipayApply, error) {
return nil, nil
})
defer guard.Unpatch()
info, err := s.realnameAlipayApply(c, mid)
ctx.Convey("Then err should be nil,info should not be nil", func(ctx convey.C) {
ctx.So(info, convey.ShouldNotBeNil)
ctx.So(err, convey.ShouldBeNil)
})
})
})
}
```
2. 基础库错误Mock示例
```Go
// 原方法(部分)
func (d *Dao) BaseInfoCache(c context.Context, mid int64) (info *model.BaseInfo, err error) {
...
conn := d.mc.Get(c)
defer conn.Close()
item, err := conn.Get(key)
if err != nil {
log.Error("conn.Get(%s) error(%v)", key, err)
return
}
...
return
}
// 测试方法(错误Mock部分)
func TestDaoBaseInfoCache(t *testing.T) {
convey.Convey("BaseInfoCache", t, func(ctx convey.C) {
...
Convey("When conn.Get gets error", func(ctx convey.C) {
guard := monkey.PatchInstanceMethod(reflect.TypeOf(d.mc), "Get", func(_ *memcache.Pool, _ context.Context) memcache.Conn {
return memcache.MockWith(memcache.ErrItemObject)
})
defer guard.Unpatch()
_, err := d.BaseInfoCache(c, mid)
ctx.Convey("Error should be equal to memcache.ErrItemObject", func(ctx convey.C) {
ctx.So(err, convey.ShouldEqual, memcache.ErrItemObject)
})
})
})
}
```
#### 注意事项
- Monkey非线程安全
- Monkey无法针对Inline方法打补丁,在测试时可以使用go test -gcflags=-l来关闭inline编译的模式(一些简单的go inline介绍戳这里)
- Monkey在一些面向安全不允许内存页写和执行同时进行的操作系统上无法工作
- 更多详情请戳:https://github.com/bouk/monkey
### Gock——HTTP请求Mock工具
#### 特性和使用条件
#### 工作原理
1. 截获任意通过 http.DefaultTransport或者自定义http.Transport对外的http.Client请求
2. 以“先进先出”原则将对外需求和预定义好的HTTP Mock池中进行匹配
3. 如果至少一个Mock被匹配,将按照2中顺序原则组成Mock的HTTP返回
4. 如果没有Mock被匹配,若实际的网络可用,将进行实际的HTTP请求。否则将返回错误
#### 特性
- 内建帮助工具实现JSON/XML简单Mock
- 支持持久的、易失的和TTL限制的Mock
- 支持HTTP Mock请求完整的正则表达式匹配
- 可通过HTTP方法,URL参数,请求头和请求体匹配
- 可扩展和可插件化的HTTP匹配规则
- 具备在Mock和实际网络模式之间切换的能力
- 具备过滤和映射HTTP请求到正确的Mock匹配的能力
- 支持映射和过滤可以更简单的掌控Mock
- 通过使用http.RoundTripper接口广泛兼容HTTP拦截器
- 可以在任意net/http兼容的Client上工作
- 网络延迟模拟(beta版本)
- 无其他依赖
#### 适用场景(建议)
任何需要进行HTTP请求的操作,建议全部用Gock进行Mock,以减少对环境的依赖。
使用示例:
1. net/http 标准库 HTTP 请求Mock
```Go
import gock "gopkg.in/h2non/gock.v1"
// 原方法
func (d *Dao) Upload(c context.Context, fileName, fileType string, expire int64, body io.Reader) (location string, err error) {
...
resp, err = d.bfsClient.Do(req) //d.bfsClient类型为*http.client
...
if resp.StatusCode != http.StatusOK {
...
}
header = resp.Header
code = header.Get("Code")
if code != strconv.Itoa(http.StatusOK) {
...
}
...
return
}
// 测试方法
func TestDaoUpload(t *testing.T) {
convey.Convey("Upload", t, func(ctx convey.C) {
...
// d.client 类型为 *http.client 根据Gock包描述需要设置http.Client的Transport情况。也可在TestMain中全局设置,则所有的HTTP请求均通过Gock来解决
d.client.Transport = gock.DefaultTransport // !注意:进行httpMock前需要对http 请求进行拦截,否则Mock失败
// HTTP请求状态和Header都正确的Mock
ctx.Convey("When everything is correct", func(ctx convey.C) {
httpMock("PUT", url).Reply(200).SetHeaders(map[string]string{
"Code": "200",
"Location": "SomePlace",
})
location, err := d.Upload(c, fileName, fileType, expire, body)
ctx.Convey("Then err should be nil.location should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(location, convey.ShouldNotBeNil)
})
})
...
// HTTP请求状态错误Mock
ctx.Convey("When http request status != 200", func(ctx convey.C) {
d.client.Transport = gock.DefaultTransport
httpMock("PUT", url).Reply(404)
_, err := d.Upload(c, fileName, fileType, expire, body)
ctx.Convey("Then err should not be nil", func(ctx convey.C) {
ctx.So(err, convey.ShouldNotBeNil)
})
})
// HTTP请求Header中Code值错误Mock
ctx.Convey("When http request Code in header != 200", func(ctx convey.C) {
d.client.Transport = gock.DefaultTransport
httpMock("PUT", url).Reply(404).SetHeaders(map[string]string{
"Code": "404",
"Location": "SomePlace",
})
_, err := d.Upload(c, fileName, fileType, expire, body)
ctx.Convey("Then err should not be nil", func(ctx convey.C) {
ctx.So(err, convey.ShouldNotBeNil)
})
})
// 由于同包内有其他进行实际HTTP请求的测试。所以再每次用例结束后,进行现场恢复(关闭Gock设置默认的Transport)
ctx.Reset(func() {
gock.OffAll()
d.client.Transport = http.DefaultClient.Transport
})
})
}
func httpMock(method, url string) *gock.Request {
r := gock.New(url)
r.Method = strings.ToUpper(method)
return r
}
```
2. blademaster库HTTP请求Mock
```Go
// 原方法
func (d *Dao) SendWechatToGroup(c context.Context, chatid, msg string) (err error) {
...
if err = d.client.Do(c, req, &res); err != nil {
...
}
if res.Code != 0 {
...
}
return
}
// 测试方法
func TestDaoSendWechatToGroup(t *testing.T) {
convey.Convey("SendWechatToGroup", t, func(ctx convey.C) {
...
// 根据Gock包描述需要设置bm.Client的Transport情况。也可在TestMain中全局设置,则所有的HTTP请求均通过Gock来解决。
// d.client 类型为 *bm.client
d.client.SetTransport(gock.DefaultTransport) // !注意:进行httpMock前需要对http 请求进行拦截,否则Mock失败
// HTTP请求状态和返回内容正常Mock
ctx.Convey("When everything gose postive", func(ctx convey.C) {
httpMock("POST", _sagaWechatURL+"/appchat/send").Reply(200).JSON(`{"code":0,"message":"0"}`)
err := d.SendWechatToGroup(c, d.c.WeChat.ChatID, msg)
...
})
// HTTP请求状态错误Mock
ctx.Convey("When http status != 200", func(ctx convey.C) {
httpMock("POST", _sagaWechatURL+"/appchat/send").Reply(404)
err := d.SendWechatToGroup(c, d.c.WeChat.ChatID, msg)
...
})
// HTTP请求返回值错误Mock
ctx.Convey("When http response code != 0", func(ctx convey.C) {
httpMock("POST", _sagaWechatURL+"/appchat/send").Reply(200).JSON(`{"code":-401,"message":"0"}`)
err := d.SendWechatToGroup(c, d.c.WeChat.ChatID, msg)
...
})
// 由于同包内有其他进行实际HTTP请求的测试。所以再每次用例结束后,进行现场恢复(关闭Gock设置默认的Transport)。
ctx.Reset(func() {
gock.OffAll()
d.client.SetTransport(http.DefaultClient.Transport)
})
})
}
func httpMock(method, url string) *gock.Request {
r := gock.New(url)
r.Method = strings.ToUpper(method)
return r
}
```
#### 注意事项
- Gock不是完全线程安全的
- 如果执行并发代码,在配置Gock和解释定制的HTTP clients时,要确保Mock已经事先声明好了来避免不需要的竞争机制
- 更多详情请戳:https://github.com/h2non/gock
### GoMock
#### 使用条件
只能对公有接口(interface)定义的代码进行Mock,并仅能在测试过程中进行
#### 使用方法
- 官方安装使用步骤
```shell
## 获取GoMock包和自动生成Mock代码工具mockgen
go get github.com/golang/mock/gomock
go install github.com/golang/mock/mockgen
## 生成mock文件
## 方法1:生成对应文件下所有interface
mockgen -source=path/to/your/interface/file.go
## 方法2:生成对应包内指定多个interface,并用逗号隔开
mockgen database/sql/driver Conn,Driver
## 示例:
mockgen -destination=$GOPATH/kratos/app/xxx/dao/dao_mock.go -package=dao kratos/app/xxx/dao DaoInterface
```
- testgen 使用步骤(GoMock生成功能已集成在Creater工具中,无需额外安装步骤即可直接使用)
```shell
## 直接给出含有接口类型定义的包路径,生成Mock文件将放在包目录下一级mock/pkgName_mock.go中
./creater --m mock absolute/path/to/your/pkg
```
- 测试代码内使用方法
```Go
// 测试用例内直接使用
// 需引入的包
import (
...
"github.com/otokaze/mock/gomock"
...
)
func TestPkgFoo(t *testing.T) {
convey.Convey("Foo", t, func(ctx convey.C) {
...
ctx.Convey("Mock Interface to test", func(ctx convey.C) {
// 1. 使用gomock.NewController新增一个控制器
mockCtrl := gomock.NewController(t)
// 2. 测试完成后关闭控制器
defer mockCtrl.Finish()
// 3. 以控制器为参数生成Mock对象
yourMock := mock.NewMockYourClient(mockCtrl)
// 4. 使用Mock对象替代原代码中的对象
yourClient = yourMock
// 5. 使用EXPECT().方法名(方法参数).Return(返回值)来构造所需输入/输出
yourMock.EXPECT().YourMethod(gomock.Any()).Return(nil)
res:= Foo(params)
...
})
...
})
}
// 可以利用Convey执行顺序方式适当调整以简化代码
func TestPkgFoo(t *testing.T) {
convey.Convey("Foo", t, func(ctx convey.C) {
...
mockCtrl := gomock.NewController(t)
yourMock := mock.NewMockYourClient(mockCtrl)
ctx.Convey("Mock Interface to test1", func(ctx convey.C) {
yourMock.EXPECT().YourMethod(gomock.Any()).Return(nil)
...
})
ctx.Convey("Mock Interface to test2", func(ctx convey.C) {
yourMock.EXPECT().YourMethod(args).Return(res)
...
})
...
ctx.Reset(func(){
mockCtrl.Finish()
})
})
}
```
#### 适用场景(建议)
1. gRPC中的Client接口
2. 也可改造现有代码构造Interface后使用(具体可配合Creater的功能进行Interface和Mock的生成)
3. 任何对接口中定义方法依赖的场景
#### 注意事项
- 如有Mock文件在包内,在执行单元测试时Mock代码会被识别进行测试。请注意Mock文件的放置。
- 更多详情请戳:https://github.com/golang/mock

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

Loading…
Cancel
Save