diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index a2161bf72..16bb0a9fb 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -36,14 +36,15 @@ jobs: - "8848:8848" - "9848:9848" polaris: - image: polarismesh/polaris-server-standalone:latest + image: polarismesh/polaris-server-standalone:v1.9.0 ports: - 8090:8090 - 8091:8091 + - 8093:8093 steps: - uses: actions/checkout@v3 - name: Set up Go - uses: actions/setup-go@v3.2.0 + uses: actions/setup-go@v3.2.1 with: go-version: ${{ matrix.go }} diff --git a/.gitignore b/.gitignore index c29907463..0384a2c2f 100644 --- a/.gitignore +++ b/.gitignore @@ -15,6 +15,10 @@ # Dependency directories (remove the comment below to include it) vendor/ +# Go workspace file +go.work +go.work.sum + # Compiled Object files, Static and Dynamic libs (Shared Objects) *.o *.a diff --git a/README_zh.md b/README_zh.md index 7bf99bf0a..230f52f02 100644 --- a/README_zh.md +++ b/README_zh.md @@ -38,18 +38,18 @@ Kratos 一套轻量级 Go 微服务框架,包含大量微服务相关功能及 * 工具链:包含大量工具链,比如 cache 代码生成,lint 工具等等; ## Features -* [APIs](https://github.com/go-kratos/examples/helloworld/helloworld) :协议通信以 HTTP/gRPC 为基础,通过 Protobuf 进行定义; -* [Errors](https://github.com/go-kratos/examples/errors/api) :通过 Protobuf 的 Enum 作为错误码定义,以及工具生成判定接口; -* [Metadata](https://github.com/go-kratos/examples/metadata) :在协议通信 HTTP/gRPC 中,通过 Middleware 规范化服务元信息传递; -* [Config](https://github.com/go-kratos/examples/config) :支持多数据源方式,进行配置合并铺平,通过 Atomic 方式支持动态配置; -* [Logger](https://github.com/go-kratos/examples/log) :标准日志接口,可方便集成三方 log 库,并可通过 fluentd 收集日志; -* [Metrics](https://github.com/go-kratos/examples/metrics) :统一指标接口,可以实现各种指标系统,默认集成 Prometheus; -* [Tracing](https://github.com/go-kratos/examples/traces) :遵循 OpenTelemetry 规范定义,以实现微服务链路追踪; -* [Encoding](encoding):支持 Accept 和 Content-Type 进行自动选择内容编码; -* [Transport](transport/transport.go) :通用的 [HTTP](https://github.com/go-kratos/examples/http/middlewares) /[gRPC](https://github.com/go-kratos/examples/middleware/main.go) 传输层,实现统一的 [Middleware](middleware) 插件支持; -* [Registry](https://github.com/go-kratos/examples/registry) :实现统一注册中心接口,可插件化对接各种注册中心; -* [Validation](https://github.com/go-kratos/examples/validate): 通过Protobuf统一定义校验规则,并同时适用于HTTP/gRPC服务. -* [SwaggerAPI](https://github.com/go-kratos/swagger-api/blob/main/examples/helloworld/server/main.go): 通过集成第三方[Swagger插件](https://github.com/go-kratos/swagger-api) 能够自动生成Swagger API json并启动一个内置的Swagger UI服务. +* [APIs](https://go-kratos.dev/docs/component/api) :协议通信以 HTTP/gRPC 为基础,通过 Protobuf 进行定义; +* [Errors](https://go-kratos.dev/docs/component/errors/) :通过 Protobuf 的 Enum 作为错误码定义,以及工具生成判定接口; +* [Metadata](https://go-kratos.dev/docs/component/metadata) :在协议通信 HTTP/gRPC 中,通过 Middleware 规范化服务元信息传递; +* [Config](https://go-kratos.dev/docs/component/config) :支持多数据源方式,进行配置合并铺平,通过 Atomic 方式支持动态配置; +* [Logger](https://go-kratos.dev/docs/component/log) :标准日志接口,可方便集成三方 log 库,并可通过 fluentd 收集日志; +* [Metrics](https://go-kratos.dev/docs/component/middleware/metrics) :统一指标接口,可以实现各种指标系统,默认集成 Prometheus; +* [Tracing](https://go-kratos.dev/docs/component/middleware/tracing) :遵循 OpenTelemetry 规范定义,以实现微服务链路追踪; +* [Encoding](https://go-kratos.dev/docs/component/encoding) :支持 Accept 和 Content-Type 进行自动选择内容编码; +* [Transport](https://go-kratos.dev/docs/component/transport/overview) :通用的 [HTTP](https://go-kratos.dev/docs/component/transport/http) /[gRPC](https://go-kratos.dev/docs/component/transport/grpc) 传输层,实现统一的 [Middleware](https://go-kratos.dev/docs/component/middleware/overview) 插件支持; +* [Registry](https://go-kratos.dev/docs/component/registry) :实现统一注册中心接口,可插件化对接各种注册中心; +* [Validation](https://go-kratos.dev/docs/component/middleware/validate): 通过Protobuf统一定义校验规则,并同时适用于HTTP/gRPC服务. +* [SwaggerAPI](https://go-kratos.dev/docs/guide/openapi): 通过集成第三方[Swagger插件](https://github.com/go-kratos/swagger-api) 能够自动生成Swagger API json并启动一个内置的Swagger UI服务. ## Getting Started ### Required diff --git a/api/metadata/server.go b/api/metadata/server.go index 445ddeb69..3438d1327 100644 --- a/api/metadata/server.go +++ b/api/metadata/server.go @@ -4,6 +4,7 @@ import ( "bytes" "compress/gzip" "context" + "errors" "fmt" "io" "sync" @@ -76,6 +77,10 @@ func (s *Server) load() error { } fdps, e := allDependency(fdp) if e != nil { + if errors.Is(e, protoregistry.NotFound) { + // Skip this service if one of its dependencies is not found. + continue + } err = e return false } diff --git a/app_test.go b/app_test.go index 80831b541..aa0a5ef72 100644 --- a/app_test.go +++ b/app_test.go @@ -3,6 +3,7 @@ package kratos import ( "context" "fmt" + "net/url" "reflect" "sync" "testing" @@ -92,6 +93,80 @@ func TestApp_Metadata(t *testing.T) { } func TestApp_Endpoint(t *testing.T) { + v := []string{"https://go-kratos.dev", "localhost"} + var endpoints []*url.URL + for _, urlStr := range v { + if endpoint, err := url.Parse(urlStr); err != nil { + t.Errorf("invalid endpoint:%v", urlStr) + } else { + endpoints = append(endpoints, endpoint) + } + } + o := New(Endpoint(endpoints...)) + if instance, err := o.buildInstance(); err != nil { + t.Error("build instance failed") + } else { + o.instance = instance + } + if !reflect.DeepEqual(o.Endpoint(), v) { + t.Errorf("Endpoint() = %v, want %v", o.Endpoint(), v) + } +} + +func TestApp_buildInstance(t *testing.T) { + want := struct { + id string + name string + version string + metadata map[string]string + endpoints []string + }{ + id: "1", + name: "kratos", + version: "v1.0.0", + metadata: map[string]string{ + "a": "1", + "b": "2", + }, + endpoints: []string{"https://go-kratos.dev", "localhost"}, + } + var endpoints []*url.URL + for _, urlStr := range want.endpoints { + if endpoint, err := url.Parse(urlStr); err != nil { + t.Errorf("invalid endpoint:%v", urlStr) + } else { + endpoints = append(endpoints, endpoint) + } + } + app := New( + ID(want.id), + Name(want.name), + Version(want.version), + Metadata(want.metadata), + Endpoint(endpoints...), + ) + if got, err := app.buildInstance(); err != nil { + t.Error("build got failed") + } else { + if got.ID != want.id { + t.Errorf("ID() = %v, want %v", got.ID, want.id) + } + if got.Name != want.name { + t.Errorf("Name() = %v, want %v", got.Name, want.name) + } + if got.Version != want.version { + t.Errorf("Version() = %v, want %v", got.Version, want.version) + } + if !reflect.DeepEqual(got.Endpoints, want.endpoints) { + t.Errorf("Endpoint() = %v, want %v", got.Endpoints, want.endpoints) + } + if !reflect.DeepEqual(got.Metadata, want.metadata) { + t.Errorf("Metadata() = %v, want %v", got.Metadata, want.metadata) + } + } +} + +func TestApp_Context(t *testing.T) { type fields struct { id string version string diff --git a/cmd/kratos/internal/project/add.go b/cmd/kratos/internal/project/add.go index 98661cd1d..f4c1b88c3 100644 --- a/cmd/kratos/internal/project/add.go +++ b/cmd/kratos/internal/project/add.go @@ -12,7 +12,7 @@ import ( ) var repoAddIgnores = []string{ - ".git", ".github", "api", "README.md", "LICENSE", "go.mod", "go.sum", "third_party", + ".git", ".github", "api", "README.md", "LICENSE", "go.mod", "go.sum", "third_party", "openapi.yaml", ".gitignore", } func (p *Project) Add(ctx context.Context, dir string, layout string, branch string, mod string) error { diff --git a/cmd/kratos/internal/proto/server/server.go b/cmd/kratos/internal/proto/server/server.go index dcc19790d..3b8a81966 100644 --- a/cmd/kratos/internal/proto/server/server.go +++ b/cmd/kratos/internal/proto/server/server.go @@ -9,6 +9,8 @@ import ( "github.com/emicklei/proto" "github.com/spf13/cobra" + "golang.org/x/text/cases" + "golang.org/x/text/language" ) // CmdServer the service command. @@ -54,7 +56,7 @@ func run(cmd *cobra.Command, args []string) { proto.WithService(func(s *proto.Service) { cs := &Service{ Package: pkg, - Service: s.Name, + Service: serviceName(s.Name), } for _, e := range s.Elements { r, ok := e.(*proto.RPC) @@ -62,7 +64,7 @@ func run(cmd *cobra.Command, args []string) { continue } cs.Methods = append(cs.Methods, &Method{ - Service: s.Name, Name: ucFirst(r.Name), Request: r.RequestType, + Service: serviceName(s.Name), Name: serviceName(r.Name), Request: r.RequestType, Reply: r.ReturnsType, Type: getMethodType(r.StreamsRequest, r.StreamsReturns), }) } @@ -103,10 +105,12 @@ func getMethodType(streamsRequest, streamsReturns bool) MethodType { return unaryType } -func ucFirst(str string) string { - if str == "" { - return "" - } +func serviceName(name string) string { + return toUpperCamelCase(strings.Split(name, ".")[0]) +} - return strings.ToUpper(str[:1]) + str[1:] +func toUpperCamelCase(s string) string { + s = strings.ReplaceAll(s, "_", " ") + s = cases.Title(language.Und, cases.NoLower).String(s) + return strings.ReplaceAll(s, " ", "") } diff --git a/cmd/kratos/internal/proto/server/server_test.go b/cmd/kratos/internal/proto/server/server_test.go index a2cbeee61..fa90f692e 100644 --- a/cmd/kratos/internal/proto/server/server_test.go +++ b/cmd/kratos/internal/proto/server/server_test.go @@ -2,7 +2,7 @@ package server import "testing" -func Test_ucFirst(t *testing.T) { +func Test_serviceName(t *testing.T) { type args struct { str string } @@ -12,40 +12,50 @@ func Test_ucFirst(t *testing.T) { want string }{ { - name: "ucFirst on lowercase words", + name: "serviceName on lowercase words", args: args{str: "helloworld"}, want: "Helloworld", }, { - name: "ucFirst on uppercase words", + name: "serviceName on uppercase words", args: args{str: "HELLOWORLD"}, want: "HELLOWORLD", }, { - name: "ucFirst on lowercase words with spaces", + name: "serviceName on lowercase words with spaces", args: args{str: "hello world"}, - want: "Hello world", + want: "HelloWorld", }, { - name: "ucFirst on uppercase words with spaces", + name: "serviceName on uppercase words with spaces", args: args{str: "HELLO WORLD"}, - want: "HELLO WORLD", + want: "HELLOWORLD", }, { - name: "ucFirst on Lower Camel Case words", + name: "serviceName on Lower Camel Case words", args: args{str: "helloWorld"}, want: "HelloWorld", }, { - name: "ucFirst on Upper Camel Case words", + name: "serviceName on Lower Camel Case words", + args: args{str: "helloWorld"}, + want: "HelloWorld", + }, + { + name: "serviceName on Upper Camel Case words", args: args{str: "HelloWorld"}, want: "HelloWorld", }, + { + name: "serviceName on Upper Camel Case words", + args: args{str: "hello_world"}, + want: "HelloWorld", + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - if got := ucFirst(tt.args.str); got != tt.want { - t.Errorf("ucFirst() = %v, want %v", got, tt.want) + if got := serviceName(tt.args.str); got != tt.want { + t.Errorf("serviceName() = %v, want %v", got, tt.want) } }) } diff --git a/cmd/kratos/internal/proto/server/template.go b/cmd/kratos/internal/proto/server/template.go index 1d60ebbad..732947ae8 100644 --- a/cmd/kratos/internal/proto/server/template.go +++ b/cmd/kratos/internal/proto/server/template.go @@ -5,6 +5,7 @@ import ( "html/template" ) +//nolint:lll var serviceTemplate = ` {{- /* delete empty line */ -}} package service @@ -34,8 +35,7 @@ func New{{ .Service }}Service() *{{ .Service }}Service { {{- $s1 := "google.protobuf.Empty" }} {{ range .Methods }} {{- if eq .Type 1 }} -func (s *{{ .Service }}Service) {{ .Name }}(ctx context.Context, req {{ if eq .Request $s1 }}*emptypb.Empty -{{ else }}*pb.{{ .Request }}{{ end }}) ({{ if eq .Reply $s1 }}*emptypb.Empty{{ else }}*pb.{{ .Reply }}{{ end }}, error) { +func (s *{{ .Service }}Service) {{ .Name }}(ctx context.Context, req {{ if eq .Request $s1 }}*emptypb.Empty{{ else }}*pb.{{ .Request }}{{ end }}) ({{ if eq .Reply $s1 }}*emptypb.Empty{{ else }}*pb.{{ .Reply }}{{ end }}, error) { return {{ if eq .Reply $s1 }}&emptypb.Empty{}{{ else }}&pb.{{ .Reply }}{}{{ end }}, nil } diff --git a/cmd/kratos/internal/run/run.go b/cmd/kratos/internal/run/run.go index a84d45ba5..ac229bab2 100644 --- a/cmd/kratos/internal/run/run.go +++ b/cmd/kratos/internal/run/run.go @@ -23,8 +23,9 @@ var CmdRun = &cobra.Command{ // Run run project. func Run(cmd *cobra.Command, args []string) { var dir string - if len(args) > 0 { - dir = args[0] + cmdArgs, programArgs := splitArgs(cmd, args) + if len(cmdArgs) > 0 { + dir = cmdArgs[0] } base, err := os.Getwd() if err != nil { @@ -63,7 +64,7 @@ func Run(cmd *cobra.Command, args []string) { dir = cmdPath[dir] } } - fd := exec.Command("go", "run", ".") + fd := exec.Command("go", append([]string{"run", "."}, programArgs...)...) fd.Stdout = os.Stdout fd.Stderr = os.Stderr fd.Dir = dir @@ -73,6 +74,14 @@ func Run(cmd *cobra.Command, args []string) { } } +func splitArgs(cmd *cobra.Command, args []string) (cmdArgs, programArgs []string) { + dashAt := cmd.ArgsLenAtDash() + if dashAt >= 0 { + return args[:dashAt], args[dashAt:] + } + return args, []string{} +} + func findCMD(base string) (map[string]string, error) { wd, err := os.Getwd() if err != nil { diff --git a/cmd/kratos/version.go b/cmd/kratos/version.go index 571688f41..50708a15c 100644 --- a/cmd/kratos/version.go +++ b/cmd/kratos/version.go @@ -1,4 +1,4 @@ package main // release is the current kratos tool version. -const release = "v2.3.1" +const release = "v2.4.1" diff --git a/cmd/protoc-gen-go-errors/version.go b/cmd/protoc-gen-go-errors/version.go index 7717f4e6b..14ef3ee0d 100644 --- a/cmd/protoc-gen-go-errors/version.go +++ b/cmd/protoc-gen-go-errors/version.go @@ -1,4 +1,4 @@ package main // release is the current protoc-gen-go-errors version. -const release = "v2.3.1" +const release = "v2.4.1" diff --git a/cmd/protoc-gen-go-http/http.go b/cmd/protoc-gen-go-http/http.go index fca4a63c1..3eb91c061 100644 --- a/cmd/protoc-gen-go-http/http.go +++ b/cmd/protoc-gen-go-http/http.go @@ -31,7 +31,13 @@ func generateFile(gen *protogen.Plugin, file *protogen.File, omitempty bool) *pr g := gen.NewGeneratedFile(filename, file.GoImportPath) g.P("// Code generated by protoc-gen-go-http. DO NOT EDIT.") g.P("// versions:") - g.P(fmt.Sprintf("// protoc-gen-go-http %s", release)) + g.P(fmt.Sprintf("// - protoc-gen-go-http %s", release)) + g.P("// - protoc ", protocVersion(gen)) + if file.Proto.GetOptions().GetDeprecated() { + g.P("// ", file.Desc.Path(), " is a deprecated file.") + } else { + g.P("// source: ", file.Desc.Path()) + } g.P() g.P("package ", file.GoPackageName) g.P() @@ -301,4 +307,16 @@ func isASCIIDigit(c byte) bool { return '0' <= c && c <= '9' } +func protocVersion(gen *protogen.Plugin) string { + v := gen.Request.GetCompilerVersion() + if v == nil { + return "(unknown)" + } + var suffix string + if s := v.GetSuffix(); s != "" { + suffix = "-" + s + } + return fmt.Sprintf("v%d.%d.%d%s", v.GetMajor(), v.GetMinor(), v.GetPatch(), suffix) +} + const deprecationComment = "// Deprecated: Do not use." diff --git a/cmd/protoc-gen-go-http/version.go b/cmd/protoc-gen-go-http/version.go index a619f8e2b..36a83f842 100644 --- a/cmd/protoc-gen-go-http/version.go +++ b/cmd/protoc-gen-go-http/version.go @@ -1,4 +1,4 @@ package main // release is the current protoc-gen-go-http version. -const release = "v2.3.1" +const release = "v2.4.1" diff --git a/config/env/env_test.go b/config/env/env_test.go index b024dbeee..6574c1d83 100644 --- a/config/env/env_test.go +++ b/config/env/env_test.go @@ -417,3 +417,13 @@ func Test_matchPrefix(t *testing.T) { }) } } + +func Test_env_watch(t *testing.T) { + prefixs := []string{"BAR", "FOO"} + source := NewSource(prefixs...) + w, err := source.Watch() + if err != nil { + t.Errorf("expect no err, got %v", err) + } + _ = w.Stop() +} diff --git a/config/file/format_test.go b/config/file/format_test.go new file mode 100644 index 000000000..509a98379 --- /dev/null +++ b/config/file/format_test.go @@ -0,0 +1,41 @@ +package file + +import "testing" + +func TestFormat(t *testing.T) { + tests := []struct { + input string + expect string + }{ + { + input: "", + expect: "", + }, + { + input: " ", + expect: "", + }, + { + input: ".", + expect: "", + }, + { + input: "a.", + expect: "", + }, + { + input: ".b", + expect: "b", + }, + { + input: "a.b", + expect: "b", + }, + } + for _, v := range tests { + content := format(v.input) + if got, want := content, v.expect; got != want { + t.Errorf("expect %v,got %v", want, got) + } + } +} diff --git a/config/options_test.go b/config/options_test.go index 3c46bc305..51e8b48e7 100644 --- a/config/options_test.go +++ b/config/options_test.go @@ -2,6 +2,7 @@ package config import ( "reflect" + "strings" "testing" ) @@ -197,3 +198,31 @@ func TestDefaultResolver(t *testing.T) { }) } } + +func TestExpand(t *testing.T) { + tests := []struct { + input string + mapping func(string) string + want string + }{ + { + input: "${a}", + mapping: func(s string) string { + return strings.ToUpper(s) + }, + want: "A", + }, + { + input: "a", + mapping: func(s string) string { + return strings.ToUpper(s) + }, + want: "a", + }, + } + for _, tt := range tests { + if got := expand(tt.input, tt.mapping); got != tt.want { + t.Errorf("expand() want: %s, got: %s", tt.want, got) + } + } +} diff --git a/config/reader_test.go b/config/reader_test.go index b8a5d23a7..f2ac1444b 100644 --- a/config/reader_test.go +++ b/config/reader_test.go @@ -208,3 +208,118 @@ func TestReader_Source(t *testing.T) { t.Fatal("[]byte(`{\"a\":{\"b\":{\"X\":1}}}`) is not equal to b") } } + +func TestCloneMap(t *testing.T) { + tests := []struct { + input map[string]interface{} + want map[string]interface{} + }{ + { + input: map[string]interface{}{ + "a": 1, + "b": "2", + "c": true, + }, + want: map[string]interface{}{ + "a": 1, + "b": "2", + "c": true, + }, + }, + { + input: map[string]interface{}{}, + want: map[string]interface{}{}, + }, + { + input: nil, + want: map[string]interface{}{}, + }, + } + for _, tt := range tests { + if got, err := cloneMap(tt.input); err != nil { + t.Errorf("expect no err, got %v", err) + } else if !reflect.DeepEqual(got, tt.want) { + t.Errorf("cloneMap(%v) = %v, want %v", tt.input, got, tt.want) + } + } +} + +func TestConvertMap(t *testing.T) { + tests := []struct { + input interface{} + want interface{} + }{ + { + input: map[string]interface{}{ + "a": 1, + "b": "2", + "c": true, + "d": []byte{65, 66, 67}, + }, + want: map[string]interface{}{ + "a": 1, + "b": "2", + "c": true, + "d": "ABC", + }, + }, + { + input: []interface{}{1, 2.0, "3", true, nil, []interface{}{1, 2.0, "3", true, nil}}, + want: []interface{}{1, 2.0, "3", true, nil, []interface{}{1, 2.0, "3", true, nil}}, + }, + { + input: []byte{65, 66, 67}, + want: "ABC", + }, + } + for _, tt := range tests { + if got := convertMap(tt.input); !reflect.DeepEqual(got, tt.want) { + t.Errorf("convertMap(%v) = %v, want %v", tt.input, got, tt.want) + } + } +} + +func TestReadValue(t *testing.T) { + m := map[string]interface{}{ + "a": 1, + "b": map[string]interface{}{ + "c": "3", + "d": map[string]interface{}{ + "e": true, + }, + }, + } + va := atomicValue{} + va.Store(1) + + vbc := atomicValue{} + vbc.Store("3") + + vbde := atomicValue{} + vbde.Store(true) + + tests := []struct { + path string + want atomicValue + }{ + { + path: "a", + want: va, + }, + { + path: "b.c", + want: vbc, + }, + { + path: "b.d.e", + want: vbde, + }, + } + for _, tt := range tests { + if got, found := readValue(m, tt.path); !found { + t.Errorf("expect found %v in %v, but not.", tt.path, m) + } else if got.Load() != tt.want.Load() { + t.Errorf("readValue(%v, %v) = %v, want %v", m, tt.path, got, tt.want) + } + } +} diff --git a/contrib/config/apollo/go.mod b/contrib/config/apollo/go.mod index 2c403798e..236b4ed3c 100644 --- a/contrib/config/apollo/go.mod +++ b/contrib/config/apollo/go.mod @@ -4,12 +4,12 @@ go 1.16 require ( github.com/apolloconfig/agollo/v4 v4.2.0 - github.com/go-kratos/kratos/v2 v2.3.1 + github.com/go-kratos/kratos/v2 v2.4.0 ) require ( github.com/spf13/viper v1.11.0 // indirect - gopkg.in/yaml.v3 v3.0.1 + gopkg.in/yaml.v3 v3.0.1 // indirect ) replace github.com/go-kratos/kratos/v2 => ../../../ diff --git a/contrib/config/consul/config_test.go b/contrib/config/consul/config_test.go index 8e952736f..a82a20369 100644 --- a/contrib/config/consul/config_test.go +++ b/contrib/config/consul/config_test.go @@ -3,7 +3,9 @@ package consul import ( "reflect" "testing" + "time" + "github.com/go-kratos/kratos/v2/config" "github.com/hashicorp/consul/api" ) @@ -99,3 +101,158 @@ func TestExtToFormat(t *testing.T) { t.Errorf("kvs[0].Format is %s", kvs[0].Format) } } + +func Test_source_Watch(t *testing.T) { + client, err := api.NewClient(&api.Config{ + Address: "127.0.0.1:8500", + }) + if err != nil { + t.Fatal(err) + } + + source, err := New(client, WithPath(testPath)) + if err != nil { + t.Fatal(err) + } + + type fields struct { + source config.Source + } + + type args struct { + key string + value string + } + + tests := []struct { + name string + fields fields + args args + want string + wantErr bool + deferFunc func(t *testing.T) + }{ + { + name: "normal", + fields: fields{source: source}, + args: args{ + key: testKey, + value: "test value", + }, + want: "test value", + wantErr: false, + deferFunc: func(t *testing.T) { + _, err := client.KV().Delete(testKey, nil) + if err != nil { + t.Error(err) + } + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if tt.deferFunc != nil { + defer tt.deferFunc(t) + } + + got, err := tt.fields.source.Watch() + if (err != nil) != tt.wantErr { + t.Errorf("Watch() error = %v, wantErr %v", err, tt.wantErr) + return + } + + time.Sleep(100 * time.Millisecond) + _, err = client.KV().Put(&api.KVPair{Key: tt.args.key, Value: []byte(tt.args.value)}, nil) + if err != nil { + t.Error(err) + } + + next, err := got.Next() + if (err != nil) != tt.wantErr { + t.Errorf("Watch() error = %v, wantErr %v", err, tt.wantErr) + return + } + + if len(next) != 1 { + t.Error("watch is error") + } + + if !reflect.DeepEqual(string(next[0].Value), tt.want) { + t.Errorf("Watch got = %v, want %v", string(next[0].Value), tt.want) + } + }) + } +} + +func Test_source_Load(t *testing.T) { + client, err := api.NewClient(&api.Config{ + Address: "127.0.0.1:8500", + }) + if err != nil { + t.Fatal(err) + } + + source, err := New(client, WithPath(testPath)) + if err != nil { + t.Fatal(err) + } + + type args struct { + key string + value string + } + type fields struct { + source config.Source + } + tests := []struct { + name string + args args + fields fields + want []*config.KeyValue + wantErr bool + deferFunc func(t *testing.T) + }{ + { + name: "normal", + args: args{ + key: testKey, + value: "test value", + }, + fields: fields{ + source: source, + }, + want: []*config.KeyValue{ + { + Key: "key", + Value: []byte("test value"), + }, + }, + deferFunc: func(t *testing.T) { + _, err1 := client.KV().Delete(testKey, nil) + if err1 != nil { + t.Error(err) + } + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if tt.deferFunc != nil { + defer tt.deferFunc(t) + } + _, err = client.KV().Put(&api.KVPair{Key: tt.args.key, Value: []byte(tt.args.value)}, nil) + if err != nil { + t.Error(err) + } + got, err := tt.fields.source.Load() + if (err != nil) != tt.wantErr { + t.Errorf("Load() error = %v, wantErr %v", err, tt.wantErr) + return + } + + if !reflect.DeepEqual(got[0], tt.want[0]) { + t.Errorf("Load() got = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/contrib/config/consul/go.mod b/contrib/config/consul/go.mod index 48fe75ed4..a251affb3 100644 --- a/contrib/config/consul/go.mod +++ b/contrib/config/consul/go.mod @@ -3,8 +3,8 @@ module github.com/go-kratos/kratos/contrib/config/consul/v2 go 1.15 require ( - github.com/go-kratos/kratos/v2 v2.3.1 - github.com/hashicorp/consul/api v1.12.0 + github.com/go-kratos/kratos/v2 v2.4.0 + github.com/hashicorp/consul/api v1.13.1 ) replace github.com/go-kratos/kratos/v2 => ../../../ diff --git a/contrib/config/consul/go.sum b/contrib/config/consul/go.sum index 69f5d5bdb..5816b336c 100644 --- a/contrib/config/consul/go.sum +++ b/contrib/config/consul/go.sum @@ -70,10 +70,10 @@ github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+ github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= -github.com/hashicorp/consul/api v1.12.0 h1:k3y1FYv6nuKyNTqj6w9gXOx5r5CfLj/k/euUeBXj1OY= -github.com/hashicorp/consul/api v1.12.0/go.mod h1:6pVBMo0ebnYdt2S3H87XhekM/HHrUoTD2XXb/VrZVy0= -github.com/hashicorp/consul/sdk v0.8.0 h1:OJtKBtEjboEZvG6AOUdh4Z1Zbyu0WcxQ0qatRrZHTVU= -github.com/hashicorp/consul/sdk v0.8.0/go.mod h1:GBvyrGALthsZObzUGsfgHZQDXjg4lOjagTIwIR1vPms= +github.com/hashicorp/consul/api v1.13.1 h1:r5cPdVFUy+pFF7nt+0ArLD9hm+E39OewJkvNdjKXcL4= +github.com/hashicorp/consul/api v1.13.1/go.mod h1:+1VcOos0TVdQFqXxphG4zmGcwQB4KVGkp1maPqnkDpE= +github.com/hashicorp/consul/sdk v0.10.0 h1:rGLEh2AWK4K0KCMvqWAz2EYxQqgciIfMagWZ0nVe5MI= +github.com/hashicorp/consul/sdk v0.10.0/go.mod h1:yPkX5Q6CsxTFMjQQDJwzeNmUUF5NUGGbrDsv9wTb8cw= github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVoDkXMzJM= @@ -126,8 +126,6 @@ github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJys github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI= 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 h1:fzU/JVNcaqHQEcVFAKeR41fkiLdIPrefOvVG1VZ96U0= -github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= @@ -200,7 +198,6 @@ golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= diff --git a/contrib/config/etcd/go.mod b/contrib/config/etcd/go.mod index 92299bf2a..fffcfd134 100644 --- a/contrib/config/etcd/go.mod +++ b/contrib/config/etcd/go.mod @@ -3,7 +3,7 @@ module github.com/go-kratos/kratos/contrib/config/etcd/v2 go 1.16 require ( - github.com/go-kratos/kratos/v2 v2.3.1 + github.com/go-kratos/kratos/v2 v2.4.0 go.etcd.io/etcd/client/v3 v3.5.4 google.golang.org/grpc v1.46.2 ) diff --git a/contrib/config/kubernetes/config_test.go b/contrib/config/kubernetes/config_test.go index bd0a605a8..6cdb1c98c 100644 --- a/contrib/config/kubernetes/config_test.go +++ b/contrib/config/kubernetes/config_test.go @@ -1,18 +1,44 @@ package kubernetes import ( + "context" "log" "path/filepath" + "reflect" + "strings" "testing" "github.com/go-kratos/kratos/v2/config" + + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/rest" + "k8s.io/client-go/tools/clientcmd" "k8s.io/client-go/util/homedir" ) +const ( + testKey = "test_config.json" + namespace = "default" + name = "test" +) + +var ( + keyPath = strings.Join([]string{namespace, name, testKey}, "/") + objectMeta = metav1.ObjectMeta{ + Name: name, + Namespace: namespace, + Labels: map[string]string{ + "app": "test", + }, + } +) + func TestSource(t *testing.T) { home := homedir.HomeDir() s := NewSource( - Namespace("mesh"), + Namespace("default"), LabelSelector(""), KubeConfig(filepath.Join(home, ".kube", "config")), ) @@ -40,3 +66,143 @@ func ExampleNewSource() { log.Panic(err) } } + +func TestConfig(t *testing.T) { + restConfig, err := rest.InClusterConfig() + home := homedir.HomeDir() + + options := []Option{ + Namespace(namespace), + LabelSelector("app=test"), + } + + if err != nil { + kubeconfig := filepath.Join(home, ".kube", "config") + restConfig, err = clientcmd.BuildConfigFromFlags("", kubeconfig) + if err != nil { + t.Fatal(err) + } + options = append(options, KubeConfig(kubeconfig)) + } + clientSet, err := kubernetes.NewForConfig(restConfig) + if err != nil { + t.Fatal(err) + } + + clientSetConfigMaps := clientSet.CoreV1().ConfigMaps(namespace) + + source := NewSource(options...) + if _, err = clientSetConfigMaps.Create(context.Background(), &v1.ConfigMap{ + ObjectMeta: objectMeta, + Data: map[string]string{ + testKey: "test config", + }, + }, metav1.CreateOptions{}); err != nil { + t.Fatal(err) + } + + defer func() { + if err = clientSetConfigMaps.Delete(context.Background(), name, metav1.DeleteOptions{}); err != nil { + t.Error(err) + } + }() + kvs, err := source.Load() + if err != nil { + t.Fatal(err) + } + if len(kvs) != 1 || kvs[0].Key != keyPath || string(kvs[0].Value) != "test config" { + t.Fatal("config error") + } + + w, err := source.Watch() + if err != nil { + t.Fatal(err) + } + defer func() { + _ = w.Stop() + }() + // create also produce an event, discard it + if _, err = w.Next(); err != nil { + t.Fatal(err) + } + + if _, err = clientSetConfigMaps.Update(context.Background(), &v1.ConfigMap{ + ObjectMeta: objectMeta, + Data: map[string]string{ + testKey: "new config", + }, + }, metav1.UpdateOptions{}); err != nil { + t.Error(err) + } + + if kvs, err = w.Next(); err != nil { + t.Fatal(err) + } + + if len(kvs) != 1 || kvs[0].Key != keyPath || string(kvs[0].Value) != "new config" { + t.Fatal("config error") + } +} + +func TestExtToFormat(t *testing.T) { + restConfig, err := rest.InClusterConfig() + home := homedir.HomeDir() + + options := []Option{ + Namespace(namespace), + LabelSelector("app=test"), + } + + if err != nil { + kubeconfig := filepath.Join(home, ".kube", "config") + restConfig, err = clientcmd.BuildConfigFromFlags("", kubeconfig) + if err != nil { + t.Fatal(err) + } + options = append(options, KubeConfig(kubeconfig)) + } + clientSet, err := kubernetes.NewForConfig(restConfig) + if err != nil { + t.Fatal(err) + } + + clientSetConfigMaps := clientSet.CoreV1().ConfigMaps(namespace) + + tc := `{"a":1}` + if _, err = clientSetConfigMaps.Create(context.Background(), &v1.ConfigMap{ + ObjectMeta: objectMeta, + Data: map[string]string{ + testKey: tc, + }, + }, metav1.CreateOptions{}); err != nil { + t.Fatal(err) + } + + defer func() { + if err = clientSetConfigMaps.Delete(context.Background(), name, metav1.DeleteOptions{}); err != nil { + t.Error(err) + } + }() + + source := NewSource(options...) + if err != nil { + t.Fatal(err) + } + + kvs, err := source.Load() + if err != nil { + t.Fatal(err) + } + if !reflect.DeepEqual(len(kvs), 1) { + t.Errorf("len(kvs) = %d", len(kvs)) + } + if !reflect.DeepEqual(keyPath, kvs[0].Key) { + t.Errorf("kvs[0].Key is %s", kvs[0].Key) + } + if !reflect.DeepEqual(tc, string(kvs[0].Value)) { + t.Errorf("kvs[0].Value is %s", kvs[0].Value) + } + if !reflect.DeepEqual("json", kvs[0].Format) { + t.Errorf("kvs[0].Format is %s", kvs[0].Format) + } +} diff --git a/contrib/config/kubernetes/go.mod b/contrib/config/kubernetes/go.mod index db846f9ce..47920e054 100644 --- a/contrib/config/kubernetes/go.mod +++ b/contrib/config/kubernetes/go.mod @@ -3,10 +3,10 @@ module github.com/go-kratos/kratos/contrib/config/kubernetes/v2 go 1.16 require ( - github.com/go-kratos/kratos/v2 v2.3.1 - k8s.io/api v0.24.1 - k8s.io/apimachinery v0.24.1 - k8s.io/client-go v0.24.1 + github.com/go-kratos/kratos/v2 v2.4.0 + k8s.io/api v0.24.3 + k8s.io/apimachinery v0.24.3 + k8s.io/client-go v0.24.3 ) replace github.com/go-kratos/kratos/v2 => ../../../ diff --git a/contrib/config/kubernetes/go.sum b/contrib/config/kubernetes/go.sum index ad2346744..797d12999 100644 --- a/contrib/config/kubernetes/go.sum +++ b/contrib/config/kubernetes/go.sum @@ -652,12 +652,12 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -k8s.io/api v0.24.1 h1:BjCMRDcyEYz03joa3K1+rbshwh1Ay6oB53+iUx2H8UY= -k8s.io/api v0.24.1/go.mod h1:JhoOvNiLXKTPQ60zh2g0ewpA+bnEYf5q44Flhquh4vQ= -k8s.io/apimachinery v0.24.1 h1:ShD4aDxTQKN5zNf8K1RQ2u98ELLdIW7jEnlO9uAMX/I= -k8s.io/apimachinery v0.24.1/go.mod h1:82Bi4sCzVBdpYjyI4jY6aHX+YCUchUIrZrXKedjd2UM= -k8s.io/client-go v0.24.1 h1:w1hNdI9PFrzu3OlovVeTnf4oHDt+FJLd9Ndluvnb42E= -k8s.io/client-go v0.24.1/go.mod h1:f1kIDqcEYmwXS/vTbbhopMUbhKp2JhOeVTfxgaCIlF8= +k8s.io/api v0.24.3 h1:tt55QEmKd6L2k5DP6G/ZzdMQKvG5ro4H4teClqm0sTY= +k8s.io/api v0.24.3/go.mod h1:elGR/XSZrS7z7cSZPzVWaycpJuGIw57j9b95/1PdJNI= +k8s.io/apimachinery v0.24.3 h1:hrFiNSA2cBZqllakVYyH/VyEh4B581bQRmqATJSeQTg= +k8s.io/apimachinery v0.24.3/go.mod h1:82Bi4sCzVBdpYjyI4jY6aHX+YCUchUIrZrXKedjd2UM= +k8s.io/client-go v0.24.3 h1:Nl1840+6p4JqkFWEW2LnMKU667BUxw03REfLAVhuKQY= +k8s.io/client-go v0.24.3/go.mod h1:AAovolf5Z9bY1wIg2FZ8LPQlEdKHjLI7ZD4rw920BJw= k8s.io/gengo v0.0.0-20210813121822-485abfe95c7c/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= diff --git a/contrib/config/nacos/config.go b/contrib/config/nacos/config.go index 54d4c92d3..c234817c5 100644 --- a/contrib/config/nacos/config.go +++ b/contrib/config/nacos/config.go @@ -4,28 +4,18 @@ import ( "context" "path/filepath" "strings" - "time" - "github.com/go-kratos/kratos/v2/config" "github.com/nacos-group/nacos-sdk-go/clients/config_client" "github.com/nacos-group/nacos-sdk-go/vo" + + "github.com/go-kratos/kratos/v2/config" ) type Option func(*options) type options struct { - endpoint string // nolint:structcheck,unused - - namespaceID string // nolint:structcheck,unused - group string dataID string - - timeoutMs uint64 - logLevel string - - logDir string - cacheDir string } // WithGroup With nacos config group. @@ -42,34 +32,6 @@ func WithDataID(dataID string) Option { } } -// WithLogDir With nacos config group. -func WithLogDir(logDir string) Option { - return func(o *options) { - o.logDir = logDir - } -} - -// WithCacheDir With nacos config cache dir. -func WithCacheDir(cacheDir string) Option { - return func(o *options) { - o.cacheDir = cacheDir - } -} - -// WithLogLevel With nacos config log level. -func WithLogLevel(logLevel string) Option { - return func(o *options) { - o.logLevel = logLevel - } -} - -// WithTimeout With nacos config timeout. -func WithTimeout(time time.Duration) Option { - return func(o *options) { - o.timeoutMs = uint64(time.Milliseconds()) - } -} - type Config struct { opts options client config_client.IConfigClient diff --git a/contrib/config/nacos/config_test.go b/contrib/config/nacos/config_test.go index 2fdc4f6b0..2d2908037 100644 --- a/contrib/config/nacos/config_test.go +++ b/contrib/config/nacos/config_test.go @@ -1,41 +1,20 @@ package config import ( - "fmt" - "net" + "reflect" "testing" "time" "github.com/nacos-group/nacos-sdk-go/clients" "github.com/nacos-group/nacos-sdk-go/common/constant" "github.com/nacos-group/nacos-sdk-go/vo" - "gopkg.in/yaml.v3" - kconfig "github.com/go-kratos/kratos/v2/config" + "github.com/go-kratos/kratos/v2/config" ) -func getIntranetIP() string { - addrs, err := net.InterfaceAddrs() - if err != nil { - return "127.0.0.1" - } - - for _, address := range addrs { - if ipnet, ok := address.(*net.IPNet); ok && !ipnet.IP.IsLoopback() { - if ipnet.IP.To4() != nil { - return ipnet.IP.String() - } - } - } - return "127.0.0.1" -} - -func TestGetConfig(t *testing.T) { - ip := getIntranetIP() - // ctx := context.Background() - +func TestConfig_Load(t *testing.T) { sc := []constant.ServerConfig{ - *constant.NewServerConfig(ip, 8848), + *constant.NewServerConfig("127.0.0.1", 8848), } cc := constant.ClientConfig{ @@ -48,7 +27,6 @@ func TestGetConfig(t *testing.T) { LogLevel: "debug", } - // a more graceful way to create naming client client, err := clients.NewConfigClient( vo.NacosClientParam{ ClientConfig: &cc, @@ -58,52 +36,177 @@ func TestGetConfig(t *testing.T) { if err != nil { t.Fatal(err) } + source := NewConfigSource(client, WithGroup("test"), WithDataID("test.yaml")) - dataID := "test.yaml" - group := "test" - _, err = client.PublishConfig(vo.ConfigParam{DataId: dataID, Group: group, Content: ` -logger: - level: info -`}) - if err != nil { - t.Fatal(err) + type fields struct { + source config.Source } - time.Sleep(1 * time.Second) - c := kconfig.New( - kconfig.WithSource( - NewConfigSource(client, WithGroup(group), WithDataID(dataID)), - ), - kconfig.WithDecoder(func(kv *kconfig.KeyValue, v map[string]interface{}) error { - return yaml.Unmarshal(kv.Value, v) - }), - ) - - if err = c.Load(); err != nil { - t.Fatal(err) + tests := []struct { + name string + fields fields + want []*config.KeyValue + wantErr bool + preFunc func(t *testing.T) + deferFunc func(t *testing.T) + }{ + { + name: "normal", + fields: fields{ + source: source, + }, + wantErr: false, + preFunc: func(t *testing.T) { + _, err = client.PublishConfig(vo.ConfigParam{DataId: "test.yaml", Group: "test", Content: "test: test"}) + if err != nil { + t.Error(err) + } + time.Sleep(time.Second * 1) + }, + deferFunc: func(t *testing.T) { + _, dErr := client.DeleteConfig(vo.ConfigParam{DataId: "test.yaml", Group: "test"}) + if dErr != nil { + t.Error(dErr) + } + }, + want: []*config.KeyValue{{ + Key: "test.yaml", + Value: []byte("test: test"), + Format: "yaml", + }}, + }, + { + name: "error", + fields: fields{ + source: source, + }, + wantErr: false, + preFunc: func(t *testing.T) { + _, err = client.PublishConfig(vo.ConfigParam{DataId: "111.yaml", Group: "notExist", Content: "test: test"}) + if err != nil { + t.Error(err) + } + time.Sleep(time.Second * 1) + }, + deferFunc: func(t *testing.T) { + _, dErr := client.DeleteConfig(vo.ConfigParam{DataId: "111.yaml", Group: "notExist"}) + if dErr != nil { + t.Error(dErr) + } + }, + want: []*config.KeyValue{{ + Key: "test.yaml", + Value: []byte{}, + Format: "yaml", + }}, + }, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + if test.preFunc != nil { + test.preFunc(t) + } + if test.deferFunc != nil { + defer test.deferFunc(t) + } + s := test.fields.source + configs, lErr := s.Load() + if (lErr != nil) != test.wantErr { + t.Errorf("Load error = %v, wantErr %v", lErr, test.wantErr) + t.Errorf("Load configs = %v", configs) + return + } + if !reflect.DeepEqual(configs, test.want) { + t.Errorf("Load configs = %v, want %v", configs, test.want) + } + }) } +} - name, err := c.Value("logger.level").String() - if err != nil { - t.Fatal(err) +func TestConfig_Watch(t *testing.T) { + sc := []constant.ServerConfig{ + *constant.NewServerConfig("127.0.0.1", 8848), } - fmt.Println("get value", name) - done := make(chan struct{}) - err = c.Watch("logger.level", func(key string, value kconfig.Value) { - fmt.Println(key, " value change", value) - done <- struct{}{} - }) - if err != nil { - t.Fatal(err) + cc := constant.ClientConfig{ + TimeoutMs: 5000, + NotLoadCacheAtStart: true, + LogDir: "/tmp/nacos/log", + CacheDir: "/tmp/nacos/cache", + RotateTime: "1h", + MaxAge: 3, + LogLevel: "debug", } - _, err = client.PublishConfig(vo.ConfigParam{DataId: dataID, Group: group, Content: ` -logger: - level: debug -`}) + client, err := clients.NewConfigClient( + vo.NacosClientParam{ + ClientConfig: &cc, + ServerConfigs: sc, + }, + ) if err != nil { t.Fatal(err) } - <-done + source := NewConfigSource(client, WithGroup("test"), WithDataID("test.yaml")) + + type fields struct { + source config.Source + } + tests := []struct { + name string + fields fields + want []*config.KeyValue + wantErr bool + processFunc func(t *testing.T, w config.Watcher) + deferFunc func(t *testing.T, w config.Watcher) + }{ + { + name: "normal", + fields: fields{ + source: source, + }, + wantErr: false, + processFunc: func(t *testing.T, w config.Watcher) { + _, pErr := client.PublishConfig(vo.ConfigParam{DataId: "test.yaml", Group: "test", Content: "test: test"}) + if pErr != nil { + t.Error(pErr) + } + }, + deferFunc: func(t *testing.T, w config.Watcher) { + _, dErr := client.DeleteConfig(vo.ConfigParam{DataId: "test.yaml", Group: "test"}) + if dErr != nil { + t.Error(dErr) + } + }, + want: []*config.KeyValue{{ + Key: "test.yaml", + Value: []byte("test: test"), + Format: "yaml", + }}, + }, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + s := test.fields.source + watch, wErr := s.Watch() + if wErr != nil { + t.Error(wErr) + return + } + if test.processFunc != nil { + test.processFunc(t, watch) + } + if test.deferFunc != nil { + defer test.deferFunc(t, watch) + } + want, nErr := watch.Next() + if (nErr != nil) != test.wantErr { + t.Errorf("Watch error = %v, wantErr %v", nErr, test.wantErr) + return + } + if !reflect.DeepEqual(want, test.want) { + t.Errorf("Watch watcher = %v, want %v", watch, test.want) + } + }) + } } diff --git a/contrib/config/nacos/go.mod b/contrib/config/nacos/go.mod index c77f6657e..7890e0587 100644 --- a/contrib/config/nacos/go.mod +++ b/contrib/config/nacos/go.mod @@ -3,9 +3,9 @@ module github.com/go-kratos/kratos/contrib/config/nacos/v2 go 1.16 require ( - github.com/go-kratos/kratos/v2 v2.3.1 + github.com/go-kratos/kratos/v2 v2.4.0 github.com/nacos-group/nacos-sdk-go v1.0.9 - gopkg.in/yaml.v3 v3.0.1 + gopkg.in/yaml.v3 v3.0.1 // indirect ) replace github.com/go-kratos/kratos/v2 => ../../../ diff --git a/contrib/config/polaris/README.md b/contrib/config/polaris/README.md new file mode 100644 index 000000000..4c2694c47 --- /dev/null +++ b/contrib/config/polaris/README.md @@ -0,0 +1,25 @@ +# Polaris Config + +```go +import ( + "log" + + "github.com/polarismesh/polaris-go" + "github.com/go-kratos/kratos/contrib/config/polaris/v2" +) + +func main() { + + configApi, err := polaris.NewConfigAPI() + if err != nil { + log.Fatalln(err) + } + + source, err := New(&configApi, WithNamespace("default"), WithFileGroup("default"), WithFileName("default.yaml")) + + if err != nil { + log.Fatalln(err) + } + source.Load() +} +``` diff --git a/contrib/config/polaris/config.go b/contrib/config/polaris/config.go new file mode 100644 index 000000000..c3c39ecee --- /dev/null +++ b/contrib/config/polaris/config.go @@ -0,0 +1,103 @@ +package config + +import ( + "errors" + "fmt" + "path/filepath" + "strings" + + "github.com/go-kratos/kratos/v2/config" + + "github.com/polarismesh/polaris-go" +) + +// Option is polaris config option. +type Option func(o *options) + +type options struct { + namespace string + fileGroup string + fileName string + configFile polaris.ConfigFile +} + +// WithNamespace with polaris config namespace +func WithNamespace(namespace string) Option { + return func(o *options) { + o.namespace = namespace + } +} + +// WithFileGroup with polaris config fileGroup +func WithFileGroup(fileGroup string) Option { + return func(o *options) { + o.fileGroup = fileGroup + } +} + +// WithFileName with polaris config fileName +func WithFileName(fileName string) Option { + return func(o *options) { + o.fileName = fileName + } +} + +type source struct { + client polaris.ConfigAPI + options *options +} + +func New(client polaris.ConfigAPI, opts ...Option) (config.Source, error) { + options := &options{ + namespace: "default", + fileGroup: "", + fileName: "", + } + + for _, opt := range opts { + opt(options) + } + + if options.fileGroup == "" { + return nil, errors.New("fileGroup invalid") + } + + if options.fileName == "" { + return nil, errors.New("fileName invalid") + } + + return &source{ + client: client, + options: options, + }, nil +} + +// Load return the config values +func (s *source) Load() ([]*config.KeyValue, error) { + configFile, err := s.client.GetConfigFile(s.options.namespace, s.options.fileGroup, s.options.fileName) + if err != nil { + fmt.Println("fail to get config.", err) + return nil, err + } + + if err != nil { + return nil, err + } + content := configFile.GetContent() + k := s.options.fileName + + s.options.configFile = configFile + + return []*config.KeyValue{ + { + Key: k, + Value: []byte(content), + Format: strings.TrimPrefix(filepath.Ext(k), "."), + }, + }, nil +} + +// Watch return the watcher +func (s *source) Watch() (config.Watcher, error) { + return newWatcher(s.options.configFile), nil +} diff --git a/contrib/config/polaris/config_test.go b/contrib/config/polaris/config_test.go new file mode 100644 index 000000000..fc864526c --- /dev/null +++ b/contrib/config/polaris/config_test.go @@ -0,0 +1,308 @@ +package config + +import ( + "encoding/json" + "errors" + "fmt" + "io/ioutil" + "net/http" + "reflect" + "strings" + "testing" + + "github.com/polarismesh/polaris-go" +) + +var ( + namespace = "default" + fileGroup = "test" + originContent = `server: + port: 8080` + updatedContent = `server: + port: 8090` + configCenterURL = "http://127.0.0.1:8090" +) + +func makeJSONRequest(uri string, data string, method string, headers map[string]string) ([]byte, error) { + client := http.Client{} + req, err := http.NewRequest(method, uri, strings.NewReader(data)) + req.Header.Add("Content-Type", "application/json") + for k, v := range headers { + req.Header.Add(k, v) + } + if err != nil { + return nil, err + } + res, err := client.Do(req) + if err != nil { + return nil, err + } + defer res.Body.Close() + return ioutil.ReadAll(res.Body) +} + +type commonRes struct { + Code int32 `json:"code"` +} + +type LoginRes struct { + Code int32 `json:"code"` + LoginResponse struct { + Token string `json:"token"` + } `json:"loginResponse"` +} + +type configClient struct { + token string +} + +func newConfigClient() (*configClient, error) { + token, err := getToken() + if err != nil { + return nil, err + } + return &configClient{ + token: token, + }, nil +} + +func getToken() (string, error) { + data, err := json.Marshal(map[string]string{ + "name": "polaris", + "password": "polaris", + }) + if err != nil { + return "", err + } + // login use default user + res, err := makeJSONRequest(fmt.Sprintf("%s/core/v1/user/login", configCenterURL), string(data), http.MethodPost, map[string]string{}) + if err != nil { + return "", nil + } + var loginRes LoginRes + if err = json.Unmarshal(res, &loginRes); err != nil { + return "", err + } + return loginRes.LoginResponse.Token, nil +} + +func (client *configClient) createConfigFile(name string) error { + data, err := json.Marshal(map[string]string{ + "name": name, + "namespace": namespace, + "group": fileGroup, + "content": originContent, + "modifyBy": "polaris", + "format": "yaml", + }) + if err != nil { + return err + } + res, err := makeJSONRequest(fmt.Sprintf("%s/config/v1/configfiles", configCenterURL), string(data), http.MethodPost, map[string]string{ + "X-Polaris-Token": client.token, + }) + if err != nil { + return err + } + + var resJSON commonRes + err = json.Unmarshal(res, &resJSON) + if err != nil { + return err + } + if resJSON.Code != 200000 { + return errors.New("create error") + } + return nil +} + +func (client *configClient) updateConfigFile(name string) error { + data, err := json.Marshal(map[string]string{ + "name": name, + "namespace": namespace, + "group": fileGroup, + "content": updatedContent, + "modifyBy": "polaris", + "format": "yaml", + }) + if err != nil { + return err + } + res, err := makeJSONRequest(fmt.Sprintf("%s/config/v1/configfiles", configCenterURL), string(data), http.MethodPut, map[string]string{ + "X-Polaris-Token": client.token, + }) + if err != nil { + return err + } + var resJSON commonRes + err = json.Unmarshal(res, &resJSON) + if err != nil { + return err + } + if resJSON.Code != 200000 { + return errors.New("update error") + } + return nil +} + +func (client *configClient) deleteConfigFile(name string) error { + data, err := json.Marshal(map[string]string{}) + if err != nil { + return err + } + url := fmt.Sprintf("%s/config/v1/configfiles?namespace=%s&group=%s&name=%s", configCenterURL, namespace, fileGroup, name) + res, err := makeJSONRequest(url, string(data), http.MethodDelete, map[string]string{ + "X-Polaris-Token": client.token, + }) + if err != nil { + return err + } + var resJSON commonRes + err = json.Unmarshal(res, &resJSON) + if err != nil { + return err + } + if resJSON.Code != 200000 { + return errors.New("delete error") + } + return nil +} + +func (client *configClient) publishConfigFile(name string) error { + data, err := json.Marshal(map[string]string{ + "namespace": namespace, + "group": fileGroup, + "fileName": name, + "name": name, + }) + if err != nil { + return err + } + res, err := makeJSONRequest(fmt.Sprintf("%s/config/v1/configfiles/release", configCenterURL), string(data), http.MethodPost, map[string]string{ + "X-Polaris-Token": client.token, + }) + if err != nil { + return err + } + var resJSON commonRes + err = json.Unmarshal(res, &resJSON) + if err != nil { + return err + } + if resJSON.Code != 200000 { + return errors.New("publish error") + } + return nil +} + +func TestConfig(t *testing.T) { + name := "test.yaml" + client, err := newConfigClient() + if err != nil { + t.Fatal(err) + } + if err = client.createConfigFile(name); err != nil { + t.Fatal(err) + } + if err = client.publishConfigFile(name); err != nil { + t.Fatal(err) + } + + // Always remember clear test resource + configAPI, err := polaris.NewConfigAPI() + if err != nil { + t.Fatal(err) + } + config, err := New(configAPI, WithNamespace(namespace), WithFileGroup(fileGroup), WithFileName(name)) + if err != nil { + t.Fatal(err) + } + kv, err := config.Load() + if err != nil { + t.Fatal(err) + } + + if len(kv) != 1 || kv[0].Key != name || string(kv[0].Value) != originContent { + t.Fatal("config error") + } + + w, err := config.Watch() + if err != nil { + t.Fatal(err) + } + defer func() { + err = client.deleteConfigFile(name) + if err != nil { + t.Fatal(err) + } + if _, err = w.Next(); err != nil { + t.Fatal(err) + } + if err = w.Stop(); err != nil { + t.Fatal(err) + } + }() + + if err = client.updateConfigFile(name); err != nil { + t.Fatal(err) + } + + if err = client.publishConfigFile(name); err != nil { + t.Fatal(err) + } + + if kv, err = w.Next(); err != nil { + t.Fatal(err) + } + + if len(kv) != 1 || kv[0].Key != name || string(kv[0].Value) != updatedContent { + t.Fatal("config error") + } +} + +func TestExtToFormat(t *testing.T) { + name := "ext.yaml" + client, err := newConfigClient() + if err != nil { + t.Fatal(err) + } + if err = client.createConfigFile(name); err != nil { + t.Fatal(err) + } + if err = client.publishConfigFile(name); err != nil { + t.Fatal(err) + } + + // Always remember clear test resource + defer func() { + if err = client.deleteConfigFile(name); err != nil { + t.Fatal(err) + } + }() + + configAPI, err := polaris.NewConfigAPI() + if err != nil { + t.Fatal(err) + } + + config, err := New(configAPI, WithNamespace(namespace), WithFileGroup(fileGroup), WithFileName(name)) + if err != nil { + t.Fatal(err) + } + kv, err := config.Load() + if err != nil { + t.Fatal(err) + } + if !reflect.DeepEqual(len(kv), 1) { + t.Errorf("len(kvs) = %d", len(kv)) + } + if !reflect.DeepEqual(name, kv[0].Key) { + t.Errorf("kvs[0].Key is %s", kv[0].Key) + } + if !reflect.DeepEqual(originContent, string(kv[0].Value)) { + t.Errorf("kvs[0].Value is %s", kv[0].Value) + } + if !reflect.DeepEqual("yaml", kv[0].Format) { + t.Errorf("kvs[0].Format is %s", kv[0].Format) + } +} diff --git a/contrib/config/polaris/go.mod b/contrib/config/polaris/go.mod new file mode 100644 index 000000000..79e1a64cc --- /dev/null +++ b/contrib/config/polaris/go.mod @@ -0,0 +1,23 @@ +module github.com/go-kratos/kratos/contrib/config/polaris/v2 + +go 1.16 + +require ( + github.com/go-kratos/kratos/v2 v2.4.0 + github.com/polarismesh/polaris-go v1.1.0 +) + +require ( + github.com/hashicorp/errwrap v1.1.0 // indirect + github.com/prometheus/client_golang v1.12.2 // indirect + github.com/prometheus/common v0.35.0 // indirect + go.uber.org/atomic v1.9.0 // indirect + go.uber.org/multierr v1.8.0 // indirect + go.uber.org/zap v1.21.0 // indirect + golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e // indirect + golang.org/x/sys v0.0.0-20220627191245-f75cf1eec38b // indirect + google.golang.org/genproto v0.0.0-20220628213854-d9e0b6570c03 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) + +replace github.com/go-kratos/kratos/v2 => ../../../ diff --git a/contrib/config/polaris/go.sum b/contrib/config/polaris/go.sum new file mode 100644 index 000000000..029908d72 --- /dev/null +++ b/contrib/config/polaris/go.sum @@ -0,0 +1,615 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= +cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= +cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= +cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= +cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= +cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= +cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= +cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= +cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= +cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= +cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= +cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= +cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= +cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= +cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= +cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= +cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= +cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= +cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= +cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= +cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= +cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= +cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= +cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= +cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= +cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= +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/StackExchange/wmi v1.2.1/go.mod h1:rcmrprowKIVzvc+NUiLncP2uuArMWLCbu9SBzvHz7e8= +github.com/agiledragon/gomonkey v2.0.2+incompatible h1:eXKi9/piiC3cjJD1658mEE2o3NjkJ5vDLgYjCQu0Xlw= +github.com/agiledragon/gomonkey v2.0.2+incompatible/go.mod h1:2NGfXu1a80LLr2cmWXGBDaHEjb1idR6+FVlX5T3D9hw= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= +github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= +github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= +github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= +github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/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/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= +github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= +github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= +github.com/go-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= +github.com/go-kratos/aegis v0.1.2/go.mod h1:jYeSQ3Gesba478zEnujOiG5QdsyF3Xk/8owFUeKcHxw= +github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= +github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= +github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/form/v4 v4.2.0/go.mod h1:q1a2BY+AQUUzhl6xA/6hBetay6dEIhMHjgvJiGo6K7U= +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/golang-jwt/jwt/v4 v4.4.1/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= +github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/gonum/blas v0.0.0-20181208220705-f22b278b28ac/go.mod h1:P32wAyui1PQ58Oce/KYkOqQv8cVw1zAapXOl+dRFGbc= +github.com/gonum/floats v0.0.0-20181209220543-c233463c7e82/go.mod h1:PxC8OnwL11+aosOB5+iEPoV3picfs8tUpkVd0pDo+Kg= +github.com/gonum/integrate v0.0.0-20181209220457-a422b5c0fdf2/go.mod h1:pDgmNM6seYpwvPos3q+zxlXMsbve6mOIPucUnUOrI7Y= +github.com/gonum/internal v0.0.0-20181124074243-f884aa714029/go.mod h1:Pu4dmpkhSyOzRwuXkOgAvijx4o+4YMUJJo9OvPYMkks= +github.com/gonum/lapack v0.0.0-20181123203213-e4cdc5a0bff9/go.mod h1:XA3DeT6rxh2EAE789SSiSJNqxPaC0aE9J8NTOI0Jo/A= +github.com/gonum/matrix v0.0.0-20181209220409-c518dec07be9/go.mod h1:0EXg4mc1CNP0HCqCz+K4ts155PXIlUywf0wqN+GfPZw= +github.com/gonum/stat v0.0.0-20181125101827-41a0da705a5b/go.mod h1:Z4GIJBJO3Wa4gD4vbwQxXXZ+WHmW6E9ixmNrwvs0iZs= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o= +github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= +github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= +github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= +github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= +github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= +github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= +github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.3/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 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= +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 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/natefinch/lumberjack v2.0.0+incompatible h1:4QJd3OLAMgj7ph+yZTuX13Ld4UpgHp07nNdFX7mqFfM= +github.com/natefinch/lumberjack v2.0.0+incompatible/go.mod h1:Wi9p2TTF5DG5oU+6YfsmYQpsTIOm0B1VNzQg9Mw6nPk= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/polarismesh/polaris-go v1.1.0 h1:nFvn3q3XaVFhzF7pBnIySrN0ZZBwvbbYXC5r2DpsQN0= +github.com/polarismesh/polaris-go v1.1.0/go.mod h1:tquawfjEKp1W3ffNJQSzhfditjjoZ7tvhOCElN7Efzs= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= +github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= +github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= +github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= +github.com/prometheus/client_golang v1.12.2 h1:51L9cDoUHVrXx4zWYlcLQIZ+d+VXHgqnYKkIuq4g/34= +github.com/prometheus/client_golang v1.12.2/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= +github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= +github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= +github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= +github.com/prometheus/common v0.35.0 h1:Eyr+Pw2VymWejHqCugNaQXkAi6KayVNxaHeu6khmFBE= +github.com/prometheus/common v0.35.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= +github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= +github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU= +github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= +github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/shirou/gopsutil/v3 v3.21.8/go.mod h1:YWp/H8Qs5fVmf17v7JNZzA0mPJ+mS2e9JdiUF9LlKzQ= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= +github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= +github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +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/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/tklauser/go-sysconf v0.3.9/go.mod h1:11DU/5sG7UexIrp/O6g35hrWzu0JxlwQ3LSFUzyeuhs= +github.com/tklauser/numcpus v0.3.0/go.mod h1:yFGUr7TUHQRAhyqBcEg0Ge34zDBAsIvJJcyE6boqnA8= +github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= +go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opentelemetry.io/otel v1.7.0/go.mod h1:5BdUoMIz5WEs0vt0CUEMtSSaTSHBBVwrhnz7+nrD5xk= +go.opentelemetry.io/otel/sdk v1.7.0/go.mod h1:uTEOTwaqIVuTGiJN7ii13Ibp75wJmYUDe374q6cZwUU= +go.opentelemetry.io/otel/trace v1.7.0/go.mod h1:fzLSB9nqR2eXzxPXb2JW9IKE+ScyXA48yyE4TNvoHqU= +go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= +go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= +go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/goleak v1.1.11-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= +go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI= +go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= +go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= +go.uber.org/multierr v1.8.0 h1:dg6GjLku4EH+249NNmoIciG9N/jURbDG+pFlTkhzIC8= +go.uber.org/multierr v1.8.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= +go.uber.org/zap v1.19.1/go.mod h1:j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI= +go.uber.org/zap v1.21.0 h1:WefMeulhovoZ2sYXz7st6K0sLj7bBhpiFaud4r4zST8= +go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= +golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= +golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= +golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= +golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210917221730-978cfadd31cf/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e h1:TsQ7F31D3bUCLeqPT0u+yjp1guoArKaNKmCr22PYgTQ= +golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220513210516-0976fa681c29/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210816074244-15123e1e1f71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220627191245-f75cf1eec38b h1:2n253B2r0pYSmEV+UNCQoPfU/FiaizQEK5Gu4Bq4JE8= +golang.org/x/sys v0.0.0-20220627191245-f75cf1eec38b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= +golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= +google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= +google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= +google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= +google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= +google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= +google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20220519153652-3a47de7e79bd/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= +google.golang.org/genproto v0.0.0-20220628213854-d9e0b6570c03 h1:W70HjnmXFJm+8RNjOpIDYW2nKsSi/af0VvIZUtYkwuU= +google.golang.org/genproto v0.0.0-20220628213854-d9e0b6570c03/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= +google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= +google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= +google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= +google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= +google.golang.org/grpc v1.46.2/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= +google.golang.org/grpc v1.47.0 h1:9n77onPX5F3qfFCqjy9dhn8PbNQsIKeVU04J9G7umt8= +google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= +google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8= +gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= +rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= +rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= diff --git a/contrib/config/polaris/polaris.yaml b/contrib/config/polaris/polaris.yaml new file mode 100644 index 000000000..fdee61568 --- /dev/null +++ b/contrib/config/polaris/polaris.yaml @@ -0,0 +1,8 @@ +global: + serverConnector: + addresses: + - 127.0.0.1:8091 +config: + configConnector: + addresses: + - 127.0.0.1:8093 diff --git a/contrib/config/polaris/watcher.go b/contrib/config/polaris/watcher.go new file mode 100644 index 000000000..dcd890556 --- /dev/null +++ b/contrib/config/polaris/watcher.go @@ -0,0 +1,80 @@ +package config + +import ( + "fmt" + "path/filepath" + "strings" + + "github.com/go-kratos/kratos/v2/config" + "github.com/go-kratos/kratos/v2/log" + + "github.com/polarismesh/polaris-go" + "github.com/polarismesh/polaris-go/pkg/model" +) + +type Watcher struct { + configFile polaris.ConfigFile + fullPath string +} + +type eventChan struct { + closed bool + event chan model.ConfigFileChangeEvent +} + +var eventChanMap = make(map[string]eventChan) + +func getFullPath(namespace string, fileGroup string, fileName string) string { + return fmt.Sprintf("%s/%s/%s", namespace, fileGroup, fileName) +} + +func recieve(event model.ConfigFileChangeEvent) { + meta := event.ConfigFileMetadata + ec := eventChanMap[getFullPath(meta.GetNamespace(), meta.GetFileGroup(), meta.GetFileName())] + defer func() { + if err := recover(); err != nil { + log.Error(err) + } + }() + if !ec.closed { + ec.event <- event + } +} + +func newWatcher(configFile polaris.ConfigFile) *Watcher { + configFile.AddChangeListener(recieve) + + fullPath := getFullPath(configFile.GetNamespace(), configFile.GetFileGroup(), configFile.GetFileName()) + if _, ok := eventChanMap[fullPath]; !ok { + eventChanMap[fullPath] = eventChan{ + closed: false, + event: make(chan model.ConfigFileChangeEvent), + } + } + w := &Watcher{ + configFile: configFile, + fullPath: fullPath, + } + return w +} + +func (w *Watcher) Next() ([]*config.KeyValue, error) { + ec := eventChanMap[w.fullPath] + event := <-ec.event + return []*config.KeyValue{ + { + Key: w.configFile.GetFileName(), + Value: []byte(event.NewValue), + Format: strings.TrimPrefix(filepath.Ext(w.configFile.GetFileName()), "."), + }, + }, nil +} + +func (w *Watcher) Stop() error { + ec := eventChanMap[w.fullPath] + if !ec.closed { + ec.closed = true + close(ec.event) + } + return nil +} diff --git a/contrib/encoding/msgpack/go.mod b/contrib/encoding/msgpack/go.mod index 4adcc0b83..9e02ad81b 100644 --- a/contrib/encoding/msgpack/go.mod +++ b/contrib/encoding/msgpack/go.mod @@ -3,7 +3,7 @@ module github.com/go-kratos/kratos/contrib/encoding/msgpack/v2 go 1.16 require ( - github.com/go-kratos/kratos/v2 v2.3.1 + github.com/go-kratos/kratos/v2 v2.4.1 github.com/vmihailenco/msgpack/v5 v5.3.5 ) diff --git a/contrib/log/aliyun/aliyun.go b/contrib/log/aliyun/aliyun.go index 46abd7e48..0c1e1987f 100644 --- a/contrib/log/aliyun/aliyun.go +++ b/contrib/log/aliyun/aliyun.go @@ -15,6 +15,7 @@ import ( type Log interface { klog.Logger GetProducer() *producer.Producer + Close() error } type aliyunLog struct { diff --git a/contrib/log/aliyun/aliyun_test.go b/contrib/log/aliyun/aliyun_test.go new file mode 100644 index 000000000..1ceafc324 --- /dev/null +++ b/contrib/log/aliyun/aliyun_test.go @@ -0,0 +1,95 @@ +package aliyun + +import ( + "testing" + + "github.com/go-kratos/kratos/v2/log" +) + +func TestWithEndpoint(t *testing.T) { + opts := new(options) + endpoint := "foo" + funcEndpoint := WithEndpoint(endpoint) + funcEndpoint(opts) + if opts.endpoint != endpoint { + t.Errorf("WithEndpoint() = %s, want %s", opts.endpoint, endpoint) + } +} + +func TestWithProject(t *testing.T) { + opts := new(options) + project := "foo" + funcProject := WithProject(project) + funcProject(opts) + if opts.project != project { + t.Errorf("WithProject() = %s, want %s", opts.project, project) + } +} + +func TestWithLogstore(t *testing.T) { + opts := new(options) + logstore := "foo" + funcLogstore := WithLogstore(logstore) + funcLogstore(opts) + if opts.logstore != logstore { + t.Errorf("WithLogatore() = %s, want %s", opts.logstore, logstore) + } +} + +func TestWithAccessKey(t *testing.T) { + opts := new(options) + accessKey := "foo" + funcAccessKey := WithAccessKey(accessKey) + funcAccessKey(opts) + if opts.accessKey != accessKey { + t.Errorf("WithAccessKey() = %s, want %s", opts.accessKey, accessKey) + } +} + +func TestWithAccessSecret(t *testing.T) { + opts := new(options) + accessSecret := "foo" + funcAccessSecret := WithAccessSecret(accessSecret) + funcAccessSecret(opts) + if opts.accessSecret != accessSecret { + t.Errorf("WithAccessSecret() = %s, want %s", opts.accessSecret, accessSecret) + } +} + +func TestLogger(t *testing.T) { + project := "foo" + logger := NewAliyunLog(WithProject(project)) + defer logger.Close() + logger.GetProducer() + flog := log.NewHelper(logger) + flog.Debug("log", "test") + flog.Info("log", "test") + flog.Warn("log", "test") + flog.Error("log", "test") +} + +func TestLog(t *testing.T) { + project := "foo" + logger := NewAliyunLog(WithProject(project)) + defer logger.Close() + err := logger.Log(log.LevelDebug, 0, int8(1), int16(2), int32(3)) + if err != nil { + t.Errorf("Log() returns error:%v", err) + } + err = logger.Log(log.LevelDebug, uint(0), uint8(1), uint16(2), uint32(3)) + if err != nil { + t.Errorf("Log() returns error:%v", err) + } + err = logger.Log(log.LevelDebug, uint(0), uint8(1), uint16(2), uint32(3)) + if err != nil { + t.Errorf("Log() returns error:%v", err) + } + err = logger.Log(log.LevelDebug, int64(0), uint64(1), float32(2), float64(3)) + if err != nil { + t.Errorf("Log() returns error:%v", err) + } + err = logger.Log(log.LevelDebug, []byte{0, 1, 2, 3}, "foo") + if err != nil { + t.Errorf("Log() returns error:%v", err) + } +} diff --git a/contrib/log/aliyun/go.mod b/contrib/log/aliyun/go.mod index 5acaf3c02..8f0908edf 100644 --- a/contrib/log/aliyun/go.mod +++ b/contrib/log/aliyun/go.mod @@ -3,8 +3,8 @@ module github.com/go-kratos/kratos/contrib/log/aliyun/v2 go 1.16 require ( - github.com/aliyun/aliyun-log-go-sdk v0.1.36 - github.com/go-kratos/kratos/v2 v2.3.1 + github.com/aliyun/aliyun-log-go-sdk v0.1.37 + github.com/go-kratos/kratos/v2 v2.4.1 google.golang.org/protobuf v1.28.0 ) diff --git a/contrib/log/aliyun/go.sum b/contrib/log/aliyun/go.sum index 2ad1b6131..91f02aeaa 100644 --- a/contrib/log/aliyun/go.sum +++ b/contrib/log/aliyun/go.sum @@ -12,8 +12,8 @@ github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuy github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/aliyun/aliyun-log-go-sdk v0.1.36 h1:ZWhKl1tBOvRn3/sIrFy8gMX+Hfq2u8mee1DeU96jqjc= -github.com/aliyun/aliyun-log-go-sdk v0.1.36/go.mod h1:1QQ59pEJiVVXqKgbHcU6FWIgxT5RKBt+CT8AiQ2bEts= +github.com/aliyun/aliyun-log-go-sdk v0.1.37 h1:GvswbgLqVOHNeMWssQ9zA+R7YVDP6arLUP92bKyGZNw= +github.com/aliyun/aliyun-log-go-sdk v0.1.37/go.mod h1:1QQ59pEJiVVXqKgbHcU6FWIgxT5RKBt+CT8AiQ2bEts= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= diff --git a/contrib/log/fluent/go.mod b/contrib/log/fluent/go.mod index 5f8202972..b8477bde6 100644 --- a/contrib/log/fluent/go.mod +++ b/contrib/log/fluent/go.mod @@ -5,7 +5,7 @@ go 1.16 require ( github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 // indirect github.com/fluent/fluent-logger-golang v1.9.0 - github.com/go-kratos/kratos/v2 v2.3.1 + github.com/go-kratos/kratos/v2 v2.4.1 github.com/kr/pretty v0.3.0 // indirect github.com/tinylib/msgp v1.1.6 // indirect ) diff --git a/contrib/log/logrus/go.mod b/contrib/log/logrus/go.mod new file mode 100644 index 000000000..fa3989f34 --- /dev/null +++ b/contrib/log/logrus/go.mod @@ -0,0 +1,11 @@ +module github.com/go-kratos/kratos/contrib/log/logrus/v2 + +go 1.16 + +require ( + github.com/go-kratos/kratos/v2 v2.4.1 + github.com/sirupsen/logrus v1.8.1 + github.com/stretchr/testify v1.7.1 +) + +replace github.com/go-kratos/kratos/v2 => ../../../ diff --git a/contrib/log/logrus/go.sum b/contrib/log/logrus/go.sum new file mode 100644 index 000000000..129b0363b --- /dev/null +++ b/contrib/log/logrus/go.sum @@ -0,0 +1,166 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/StackExchange/wmi v1.2.1/go.mod h1:rcmrprowKIVzvc+NUiLncP2uuArMWLCbu9SBzvHz7e8= +github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= +github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/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/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/go-kratos/aegis v0.1.2/go.mod h1:jYeSQ3Gesba478zEnujOiG5QdsyF3Xk/8owFUeKcHxw= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= +github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/form/v4 v4.2.0/go.mod h1:q1a2BY+AQUUzhl6xA/6hBetay6dEIhMHjgvJiGo6K7U= +github.com/golang-jwt/jwt/v4 v4.4.1/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= +github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= +github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= +github.com/shirou/gopsutil/v3 v3.21.8/go.mod h1:YWp/H8Qs5fVmf17v7JNZzA0mPJ+mS2e9JdiUF9LlKzQ= +github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= +github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/tklauser/go-sysconf v0.3.9/go.mod h1:11DU/5sG7UexIrp/O6g35hrWzu0JxlwQ3LSFUzyeuhs= +github.com/tklauser/numcpus v0.3.0/go.mod h1:yFGUr7TUHQRAhyqBcEg0Ge34zDBAsIvJJcyE6boqnA8= +go.opentelemetry.io/otel v1.7.0/go.mod h1:5BdUoMIz5WEs0vt0CUEMtSSaTSHBBVwrhnz7+nrD5xk= +go.opentelemetry.io/otel/sdk v1.7.0/go.mod h1:uTEOTwaqIVuTGiJN7ii13Ibp75wJmYUDe374q6cZwUU= +go.opentelemetry.io/otel/trace v1.7.0/go.mod h1:fzLSB9nqR2eXzxPXb2JW9IKE+ScyXA48yyE4TNvoHqU= +go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220513210516-0976fa681c29/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210816074244-15123e1e1f71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220412211240-33da011f77ad h1:ntjMns5wyP/fN65tdBD4g8J5w8n015+iIIs9rtjXkY0= +golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20220519153652-3a47de7e79bd/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= +google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= +google.golang.org/grpc v1.46.2/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0 h1:hjy8E9ON/egN1tAYqKb61G10WtihqetD4sz2H+8nIeA= +gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +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= diff --git a/contrib/log/logrus/logrus.go b/contrib/log/logrus/logrus.go new file mode 100644 index 000000000..b08ba6e13 --- /dev/null +++ b/contrib/log/logrus/logrus.go @@ -0,0 +1,71 @@ +package logrus + +import ( + "github.com/go-kratos/kratos/v2/log" + "github.com/sirupsen/logrus" +) + +var _ log.Logger = (*Logger)(nil) + +type Logger struct { + log *logrus.Logger +} + +func NewLogger(logger *logrus.Logger) log.Logger { + return &Logger{ + log: logger, + } +} + +func (l *Logger) Log(level log.Level, keyvals ...interface{}) (err error) { + var ( + logrusLevel logrus.Level + fields logrus.Fields = make(map[string]interface{}) + msg string + ) + + switch level { + case log.LevelDebug: + logrusLevel = logrus.DebugLevel + case log.LevelInfo: + logrusLevel = logrus.InfoLevel + case log.LevelWarn: + logrusLevel = logrus.WarnLevel + case log.LevelError: + logrusLevel = logrus.ErrorLevel + case log.LevelFatal: + logrusLevel = logrus.FatalLevel + default: + logrusLevel = logrus.DebugLevel + } + + if logrusLevel > l.log.Level { + return + } + + if len(keyvals) == 0 { + return nil + } + if len(keyvals)%2 != 0 { + keyvals = append(keyvals, "") + } + for i := 0; i < len(keyvals); i += 2 { + key, ok := keyvals[i].(string) + if !ok { + continue + } + if key == logrus.FieldKeyMsg { + msg, _ = keyvals[i+1].(string) + continue + } + fields[key] = keyvals[i+1] + } + + if len(fields) > 0 { + l.log.WithFields(fields).Log(logrusLevel, msg) + } else { + l.log.Log(logrusLevel, msg) + } + + return +} diff --git a/contrib/log/logrus/logrus_test.go b/contrib/log/logrus/logrus_test.go new file mode 100644 index 000000000..3dcd6e64f --- /dev/null +++ b/contrib/log/logrus/logrus_test.go @@ -0,0 +1,64 @@ +package logrus + +import ( + "bytes" + "strings" + "testing" + + "github.com/go-kratos/kratos/v2/log" + "github.com/sirupsen/logrus" + "github.com/stretchr/testify/assert" +) + +func TestLoggerLog(t *testing.T) { + tests := map[string]struct { + level logrus.Level + formatter logrus.Formatter + logLevel log.Level + kvs []interface{} + want string + }{ + "json format": { + level: logrus.InfoLevel, + formatter: &logrus.JSONFormatter{}, + logLevel: log.LevelInfo, + kvs: []interface{}{"case", "json format", "msg", "1"}, + want: `{"case":"json format","level":"info","msg":"1"`, + }, + "level unmatch": { + level: logrus.InfoLevel, + formatter: &logrus.JSONFormatter{}, + logLevel: log.LevelDebug, + kvs: []interface{}{"case", "level unmatch", "msg", "1"}, + want: "", + }, + "fatal level": { + level: logrus.InfoLevel, + formatter: &logrus.JSONFormatter{}, + logLevel: log.LevelFatal, + kvs: []interface{}{"case", "json format", "msg", "1"}, + want: `{"case":"json format","level":"fatal","msg":"1"`, + }, + "no tags": { + level: logrus.InfoLevel, + formatter: &logrus.JSONFormatter{}, + logLevel: log.LevelInfo, + kvs: []interface{}{"msg", "1"}, + want: `{"level":"info","msg":"1"`, + }, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + output := new(bytes.Buffer) + logger := logrus.New() + logger.Level = test.level + logger.Out = output + logger.Formatter = test.formatter + wrapped := NewLogger(logger) + _ = wrapped.Log(test.logLevel, test.kvs...) + + assert.True(t, strings.HasPrefix(output.String(), test.want)) + }) + } +} diff --git a/contrib/log/tencent/go.mod b/contrib/log/tencent/go.mod new file mode 100644 index 000000000..feb39f170 --- /dev/null +++ b/contrib/log/tencent/go.mod @@ -0,0 +1,9 @@ +module github.com/go-kratos/kratos/contrib/log/tencent/v2 + +go 1.16 + +require ( + github.com/go-kratos/kratos/v2 v2.4.1 + github.com/tencentcloud/tencentcloud-cls-sdk-go v1.0.2 + google.golang.org/protobuf v1.28.0 +) diff --git a/contrib/log/tencent/go.sum b/contrib/log/tencent/go.sum new file mode 100644 index 000000000..b4b87442e --- /dev/null +++ b/contrib/log/tencent/go.sum @@ -0,0 +1,189 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/StackExchange/wmi v1.2.1/go.mod h1:rcmrprowKIVzvc+NUiLncP2uuArMWLCbu9SBzvHz7e8= +github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= +github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/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/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/frankban/quicktest v1.14.0 h1:+cqqvzZV87b4adx/5ayVOaYZ2CrvM4ejQvUdBzPPUss= +github.com/frankban/quicktest v1.14.0/go.mod h1:NeW+ay9A/U67EYXNFA1nPE8e/tnQv/09mUdL/ijj8og= +github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/go-kratos/aegis v0.1.2/go.mod h1:jYeSQ3Gesba478zEnujOiG5QdsyF3Xk/8owFUeKcHxw= +github.com/go-kratos/kratos/v2 v2.4.1 h1:NFQy8Ha4Xu6T3Q40JlKzspvlMa5IGvIHhJw5+sqyV4c= +github.com/go-kratos/kratos/v2 v2.4.1/go.mod h1:5acyLj4EgY428AJnZl2EwCrMV1OVlttQFBum+SghMiA= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= +github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/form/v4 v4.2.0/go.mod h1:q1a2BY+AQUUzhl6xA/6hBetay6dEIhMHjgvJiGo6K7U= +github.com/golang-jwt/jwt/v4 v4.4.1/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o= +github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= +github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= +github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= +github.com/klauspost/compress v1.15.1 h1:y9FcTHGyrebwfP0ZZqFiaxTaiDnUrGkJkI+f583BL1A= +github.com/klauspost/compress v1.15.1/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= +github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/pierrec/lz4 v2.6.1+incompatible h1:9UY3+iC23yxF0UfGaYrGplQ+79Rg+h/q9FV9ix19jjM= +github.com/pierrec/lz4 v2.6.1+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= +github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k= +github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/shirou/gopsutil/v3 v3.21.8/go.mod h1:YWp/H8Qs5fVmf17v7JNZzA0mPJ+mS2e9JdiUF9LlKzQ= +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= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/tencentcloud/tencentcloud-cls-sdk-go v1.0.2 h1:29QoDxUqlFWd0Pgyt4lqGTGMNvJGxVQ3lRx1haRaPnw= +github.com/tencentcloud/tencentcloud-cls-sdk-go v1.0.2/go.mod h1:WU+0TXfVbSctEsUUf4KmIKnfr+tknbjcsnx/TrEIPH4= +github.com/tklauser/go-sysconf v0.3.9/go.mod h1:11DU/5sG7UexIrp/O6g35hrWzu0JxlwQ3LSFUzyeuhs= +github.com/tklauser/numcpus v0.3.0/go.mod h1:yFGUr7TUHQRAhyqBcEg0Ge34zDBAsIvJJcyE6boqnA8= +go.opentelemetry.io/otel v1.7.0/go.mod h1:5BdUoMIz5WEs0vt0CUEMtSSaTSHBBVwrhnz7+nrD5xk= +go.opentelemetry.io/otel/sdk v1.7.0/go.mod h1:uTEOTwaqIVuTGiJN7ii13Ibp75wJmYUDe374q6cZwUU= +go.opentelemetry.io/otel/trace v1.7.0/go.mod h1:fzLSB9nqR2eXzxPXb2JW9IKE+ScyXA48yyE4TNvoHqU= +go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= +go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= +go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220513210516-0976fa681c29/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210816074244-15123e1e1f71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20220519153652-3a47de7e79bd/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= +google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= +google.golang.org/grpc v1.46.2/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= +google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0 h1:hjy8E9ON/egN1tAYqKb61G10WtihqetD4sz2H+8nIeA= +gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +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= diff --git a/contrib/log/tencent/tencent.go b/contrib/log/tencent/tencent.go new file mode 100644 index 000000000..5d7267f26 --- /dev/null +++ b/contrib/log/tencent/tencent.go @@ -0,0 +1,155 @@ +package tencent + +import ( + "encoding/json" + "fmt" + "strconv" + "time" + + log "github.com/go-kratos/kratos/v2/log" + cls "github.com/tencentcloud/tencentcloud-cls-sdk-go" + "google.golang.org/protobuf/proto" +) + +type Logger interface { + log.Logger + GetProducer() *cls.AsyncProducerClient + + Close() error +} + +type tencentLog struct { + producer *cls.AsyncProducerClient + opts *options +} + +func (log *tencentLog) GetProducer() *cls.AsyncProducerClient { + return log.producer +} + +type options struct { + topicID string + accessKey string + accessSecret string + endpoint string +} + +func defaultOptions() *options { + return &options{} +} + +func WithEndpoint(endpoint string) Option { + return func(cls *options) { + cls.endpoint = endpoint + } +} + +func WithTopicID(topicID string) Option { + return func(cls *options) { + cls.topicID = topicID + } +} + +func WithAccessKey(ak string) Option { + return func(cls *options) { + cls.accessKey = ak + } +} + +func WithAccessSecret(as string) Option { + return func(cls *options) { + cls.accessSecret = as + } +} + +type Option func(cls *options) + +func (log *tencentLog) Close() error { + err := log.producer.Close(5000) + return err +} + +func (log *tencentLog) Log(level log.Level, keyvals ...interface{}) error { + buf := level.String() + levelTitle := "level" + contents := make([]*cls.Log_Content, 0) + + contents = append(contents, &cls.Log_Content{ + Key: &levelTitle, + Value: &buf, + }) + for i := 0; i < len(keyvals); i += 2 { + key := toString(keyvals[i]) + value := toString(keyvals[i+1]) + contents = append(contents, &cls.Log_Content{ + Key: &key, + Value: &value, + }) + } + + logInst := &cls.Log{ + Time: proto.Int64(time.Now().Unix()), + Contents: contents, + } + err := log.producer.SendLog(log.opts.topicID, logInst, nil) + return err +} + +func NewLogger(options ...Option) (Logger, error) { + opts := defaultOptions() + for _, o := range options { + o(opts) + } + producerConfig := cls.GetDefaultAsyncProducerClientConfig() + producerConfig.AccessKeyID = opts.accessKey + producerConfig.AccessKeySecret = opts.accessSecret + producerConfig.Endpoint = opts.endpoint + producerInst, err := cls.NewAsyncProducerClient(producerConfig) + if err != nil { + return nil, err + } + return &tencentLog{ + producer: producerInst, + opts: opts, + }, nil +} + +// toString any type to string +func toString(v interface{}) string { + var key string + if v == nil { + return key + } + switch v := v.(type) { + case float64: + key = strconv.FormatFloat(v, 'f', -1, 64) + case float32: + key = strconv.FormatFloat(float64(v), 'f', -1, 64) + case int: + key = strconv.Itoa(v) + case uint: + key = strconv.Itoa(int(v)) + case int8: + key = strconv.Itoa(int(v)) + case uint8: + key = strconv.Itoa(int(v)) + case int16: + key = strconv.Itoa(int(v)) + case uint16: + key = strconv.Itoa(int(v)) + case int32: + key = strconv.Itoa(int(v)) + case uint32: + key = strconv.Itoa(int(v)) + case int64: + key = strconv.FormatInt(v, 10) + case uint64: + key = strconv.FormatUint(v, 10) + case fmt.Stringer: + key = v.String() + default: + newValue, _ := json.Marshal(v) + key = string(newValue) + } + return key +} diff --git a/contrib/log/tencent/tencent_test.go b/contrib/log/tencent/tencent_test.go new file mode 100644 index 000000000..b37c1b22c --- /dev/null +++ b/contrib/log/tencent/tencent_test.go @@ -0,0 +1,103 @@ +package tencent + +import ( + "testing" + + "github.com/go-kratos/kratos/v2/log" +) + +func TestWithEndpoint(t *testing.T) { + opts := new(options) + endpoint := "eee" + funcEndpoint := WithEndpoint(endpoint) + funcEndpoint(opts) + if opts.endpoint != "eee" { + t.Errorf("WithEndpoint() = %s, want %s", opts.endpoint, endpoint) + } +} + +func TestWithTopicId(t *testing.T) { + opts := new(options) + topicID := "ee" + funcTopicID := WithTopicID(topicID) + funcTopicID(opts) + if opts.topicID != "ee" { + t.Errorf("WithTopicId() = %s, want %s", opts.endpoint, topicID) + } +} + +func TestWithAccessKey(t *testing.T) { + opts := new(options) + accessKey := "ee" + funcAccessKey := WithAccessKey(accessKey) + funcAccessKey(opts) + if opts.accessKey != "ee" { + t.Errorf("WithAccessKey() = %s, want %s", opts.endpoint, accessKey) + } +} + +func TestWithAccessSecret(t *testing.T) { + opts := new(options) + accessSecret := "ee" + funcAccessSecret := WithAccessSecret(accessSecret) + funcAccessSecret(opts) + if opts.accessSecret != "ee" { + t.Errorf("WithAccessSecret() = %s, want %s", opts.accessSecret, accessSecret) + } +} + +func TestTestLogger(t *testing.T) { + topicID := "aa" + logger, err := NewLogger( + WithTopicID(topicID), + WithEndpoint("ap-shanghai.cls.tencentcs.com"), + WithAccessKey("a"), + WithAccessSecret("b"), + ) + if err != nil { + t.Error(err) + return + } + defer logger.Close() + logger.GetProducer() + flog := log.NewHelper(logger) + flog.Debug("log", "test") + flog.Info("log", "test") + flog.Warn("log", "test") + flog.Error("log", "test") +} + +func TestLog(t *testing.T) { + topicID := "foo" + logger, err := NewLogger( + WithTopicID(topicID), + WithEndpoint("ap-shanghai.cls.tencentcs.com"), + WithAccessKey("a"), + WithAccessSecret("b"), + ) + if err != nil { + t.Error(err) + return + } + defer logger.Close() + err = logger.Log(log.LevelDebug, 0, int8(1), int16(2), int32(3)) + if err != nil { + t.Errorf("Log() returns error:%v", err) + } + err = logger.Log(log.LevelDebug, uint(0), uint8(1), uint16(2), uint32(3)) + if err != nil { + t.Errorf("Log() returns error:%v", err) + } + err = logger.Log(log.LevelDebug, uint(0), uint8(1), uint16(2), uint32(3)) + if err != nil { + t.Errorf("Log() returns error:%v", err) + } + err = logger.Log(log.LevelDebug, int64(0), uint64(1), float32(2), float64(3)) + if err != nil { + t.Errorf("Log() returns error:%v", err) + } + err = logger.Log(log.LevelDebug, []byte{0, 1, 2, 3}, "foo") + if err != nil { + t.Errorf("Log() returns error:%v", err) + } +} diff --git a/contrib/log/zap/go.mod b/contrib/log/zap/go.mod index 43b1181c0..8d5d2c7e6 100644 --- a/contrib/log/zap/go.mod +++ b/contrib/log/zap/go.mod @@ -3,7 +3,7 @@ module github.com/go-kratos/kratos/contrib/log/zap/v2 go 1.16 require ( - github.com/go-kratos/kratos/v2 v2.3.1 + github.com/go-kratos/kratos/v2 v2.4.1 go.uber.org/zap v1.21.0 ) diff --git a/contrib/metrics/datadog/go.mod b/contrib/metrics/datadog/go.mod index 8ce027680..4d06c42c5 100644 --- a/contrib/metrics/datadog/go.mod +++ b/contrib/metrics/datadog/go.mod @@ -5,7 +5,7 @@ go 1.16 require ( github.com/DataDog/datadog-go v4.8.3+incompatible github.com/Microsoft/go-winio v0.5.2 // indirect - github.com/go-kratos/kratos/v2 v2.3.1 + github.com/go-kratos/kratos/v2 v2.4.1 ) replace github.com/go-kratos/kratos/v2 => ../../../ diff --git a/contrib/metrics/prometheus/counter_test.go b/contrib/metrics/prometheus/counter_test.go new file mode 100644 index 000000000..f84b5253c --- /dev/null +++ b/contrib/metrics/prometheus/counter_test.go @@ -0,0 +1,65 @@ +package prometheus + +import ( + "bytes" + "fmt" + "testing" + + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/common/expfmt" +) + +func gatherLatest(reg *prometheus.Registry) (result string, err error) { + mfs, err := reg.Gather() + if err != nil { + return "", err + } + buf := &bytes.Buffer{} + + enc := expfmt.NewEncoder(buf, expfmt.FmtText) + for _, mf := range mfs { + if err = enc.Encode(mf); err != nil { + return "", err + } + } + return buf.String(), nil +} + +func TestCounter(t *testing.T) { + expect := `# HELP test_request_test_metric test +# TYPE test_request_test_metric counter +test_request_test_metric{code="test",kind="test",operation="test",reason="test"} %d +` + + counterVec := prometheus.NewCounterVec(prometheus.CounterOpts{ + Namespace: "test", + Name: "test_metric", + Subsystem: "request", + Help: "test", + }, []string{"kind", "operation", "code", "reason"}) + + counter := NewCounter(counterVec) + counter.With("test", "test", "test", "test").Inc() + + reg := prometheus.NewRegistry() + reg.MustRegister(counterVec) + + result, err := gatherLatest(reg) + if err != nil { + t.Fatal(err) + } + + if result != fmt.Sprintf(expect, 1) { + t.Fatal("metrics error") + } + + counter.With("test", "test", "test", "test").Add(10) + result, err = gatherLatest(reg) + if err != nil { + t.Fatal(err) + } + + if result != fmt.Sprintf(expect, 11) { + t.Fatal("metrics error") + } +} diff --git a/contrib/metrics/prometheus/gauge_test.go b/contrib/metrics/prometheus/gauge_test.go new file mode 100644 index 000000000..31744a5a4 --- /dev/null +++ b/contrib/metrics/prometheus/gauge_test.go @@ -0,0 +1,55 @@ +package prometheus + +import ( + "fmt" + "testing" + + "github.com/prometheus/client_golang/prometheus" +) + +func TestGuage(t *testing.T) { + expect := `# HELP test_request_test_guage_metric test +# TYPE test_request_test_guage_metric gauge +test_request_test_guage_metric{code="test",kind="test",operation="test",reason="test"} %d +` + + guageVec := prometheus.NewGaugeVec(prometheus.GaugeOpts{ + Namespace: "test", + Name: "test_guage_metric", + Subsystem: "request", + Help: "test", + }, []string{"kind", "operation", "code", "reason"}) + + guage := NewGauge(guageVec) + guage.With("test", "test", "test", "test").Set(1) + + reg := prometheus.NewRegistry() + reg.MustRegister(guageVec) + + result, err := gatherLatest(reg) + if err != nil { + t.Fatal(err) + } + + if result != fmt.Sprintf(expect, 1) { + t.Fatal("metrics error") + } + + guage.With("test", "test", "test", "test").Add(1) + result, err = gatherLatest(reg) + if err != nil { + t.Fatal(err) + } + if result != fmt.Sprintf(expect, 2) { + t.Fatal("metrics error") + } + + guage.With("test", "test", "test", "test").Sub(1) + result, err = gatherLatest(reg) + if err != nil { + t.Fatal(err) + } + if result != fmt.Sprintf(expect, 1) { + t.Fatal("metrics error") + } +} diff --git a/contrib/metrics/prometheus/go.mod b/contrib/metrics/prometheus/go.mod index 8c6f24aae..ff2f8af2e 100644 --- a/contrib/metrics/prometheus/go.mod +++ b/contrib/metrics/prometheus/go.mod @@ -3,8 +3,9 @@ module github.com/go-kratos/kratos/contrib/metrics/prometheus/v2 go 1.16 require ( - github.com/go-kratos/kratos/v2 v2.3.1 + github.com/go-kratos/kratos/v2 v2.4.1 github.com/prometheus/client_golang v1.12.2 + github.com/prometheus/common v0.37.0 ) replace github.com/go-kratos/kratos/v2 => ../../../ diff --git a/contrib/metrics/prometheus/go.sum b/contrib/metrics/prometheus/go.sum index b7e71ac8d..559a32af3 100644 --- a/contrib/metrics/prometheus/go.sum +++ b/contrib/metrics/prometheus/go.sum @@ -74,10 +74,12 @@ github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2 github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= +github.com/go-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= github.com/go-kratos/aegis v0.1.2/go.mod h1:jYeSQ3Gesba478zEnujOiG5QdsyF3Xk/8owFUeKcHxw= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= +github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= @@ -183,6 +185,7 @@ github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXP github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= +github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= github.com/prometheus/client_golang v1.12.2 h1:51L9cDoUHVrXx4zWYlcLQIZ+d+VXHgqnYKkIuq4g/34= github.com/prometheus/client_golang v1.12.2/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= @@ -193,8 +196,9 @@ github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6T github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= -github.com/prometheus/common v0.32.1 h1:hWIdL3N2HoUx3B8j3YN9mWor0qhY/NlEKZEaXxuIRh4= github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= +github.com/prometheus/common v0.37.0 h1:ccBbHCgIiT9uSoFY0vX8H3zsNR5eLt17/RQLUvn8pXE= +github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= @@ -296,12 +300,15 @@ golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81R golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -353,11 +360,14 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210816074244-15123e1e1f71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220412211240-33da011f77ad h1:ntjMns5wyP/fN65tdBD4g8J5w8n015+iIIs9rtjXkY0= golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -365,6 +375,7 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= diff --git a/contrib/metrics/prometheus/histogram_test.go b/contrib/metrics/prometheus/histogram_test.go new file mode 100644 index 000000000..7f2a511dc --- /dev/null +++ b/contrib/metrics/prometheus/histogram_test.go @@ -0,0 +1,77 @@ +package prometheus + +import ( + "fmt" + "strconv" + "testing" + + "github.com/prometheus/client_golang/prometheus" +) + +func intToFloatString(in int) string { + return strconv.FormatFloat(float64(in), 'f', -1, 64) +} + +func TestHistogram(t *testing.T) { + expect := `# HELP test_request_test_metrics test +# TYPE test_request_test_metrics histogram +test_request_test_metrics_bucket{code="test",kind="test",operation="test",reason="test",le="0.05"} %s +test_request_test_metrics_bucket{code="test",kind="test",operation="test",reason="test",le="0.1"} %s +test_request_test_metrics_bucket{code="test",kind="test",operation="test",reason="test",le="0.25"} %s +test_request_test_metrics_bucket{code="test",kind="test",operation="test",reason="test",le="0.5"} %s +test_request_test_metrics_bucket{code="test",kind="test",operation="test",reason="test",le="1"} %s +test_request_test_metrics_bucket{code="test",kind="test",operation="test",reason="test",le="+Inf"} %s +test_request_test_metrics_sum{code="test",kind="test",operation="test",reason="test"} %s +test_request_test_metrics_count{code="test",kind="test",operation="test",reason="test"} %s +` + + histogramVec := prometheus.NewHistogramVec(prometheus.HistogramOpts{ + Namespace: "test", + Name: "test_metrics", + Subsystem: "request", + Help: "test", + Buckets: []float64{0.05, 0.1, 0.250, 0.5, 1}, + }, []string{"kind", "operation", "code", "reason"}) + + histogram := NewHistogram(histogramVec) + histogram.With("test", "test", "test", "test").Observe(0.5) + + reg := prometheus.NewRegistry() + reg.MustRegister(histogramVec) + + result, err := gatherLatest(reg) + if err != nil { + t.Fatal(err) + } + + if result != fmt.Sprintf(expect, + intToFloatString(0), + intToFloatString(0), + intToFloatString(0), + intToFloatString(1), + intToFloatString(1), + intToFloatString(1), + "0.5", + intToFloatString(1)) { + t.Fatal("metrics error") + } + + histogram.With("test", "test", "test", "test").Observe(0.1) + result, err = gatherLatest(reg) + + if err != nil { + t.Fatal(err) + } + if result != fmt.Sprintf(expect, + intToFloatString(0), + intToFloatString(1), + intToFloatString(1), + intToFloatString(2), + intToFloatString(2), + intToFloatString(2), + "0.6", + intToFloatString(2), + ) { + t.Fatal("metrics error") + } +} diff --git a/contrib/metrics/prometheus/summary_test.go b/contrib/metrics/prometheus/summary_test.go new file mode 100644 index 000000000..3c93ae948 --- /dev/null +++ b/contrib/metrics/prometheus/summary_test.go @@ -0,0 +1,47 @@ +package prometheus + +import ( + "fmt" + "testing" + + "github.com/prometheus/client_golang/prometheus" +) + +func TestSummary(t *testing.T) { + expect := `# HELP test_request_test_metric test +# TYPE test_request_test_metric summary +test_request_test_metric_sum{code="test",kind="test",operation="test",reason="test"} %s +test_request_test_metric_count{code="test",kind="test",operation="test",reason="test"} %s +` + + summaryVec := prometheus.NewSummaryVec(prometheus.SummaryOpts{ + Namespace: "test", + Name: "test_metric", + Subsystem: "request", + Help: "test", + }, []string{"kind", "operation", "code", "reason"}) + + summary := NewSummary(summaryVec) + summary.With("test", "test", "test", "test").Observe(1) + + reg := prometheus.NewRegistry() + reg.MustRegister(summaryVec) + result, err := gatherLatest(reg) + if err != nil { + t.Fatal(err) + } + + if result != fmt.Sprintf(expect, intToFloatString(1), intToFloatString(1)) { + t.Fatal("metrics error") + } + summary.With("test", "test", "test", "test").Observe(10) + + result, err = gatherLatest(reg) + if err != nil { + t.Fatal(err) + } + + if result != fmt.Sprintf(expect, intToFloatString(11), intToFloatString(2)) { + t.Fatal("metrics error") + } +} diff --git a/contrib/opensergo/go.mod b/contrib/opensergo/go.mod index 8849cb45f..fa64c2ca4 100644 --- a/contrib/opensergo/go.mod +++ b/contrib/opensergo/go.mod @@ -3,7 +3,7 @@ module github.com/go-kratos/kratos/contrib/opensergo/v2 go 1.17 require ( - github.com/go-kratos/kratos/v2 v2.3.1 + github.com/go-kratos/kratos/v2 v2.4.1 github.com/opensergo/opensergo-go v0.0.0-20220331070310-e5b01fee4d1c golang.org/x/net v0.0.0-20220520000938-2e3eb7b945c2 google.golang.org/genproto v0.0.0-20220519153652-3a47de7e79bd diff --git a/contrib/registry/consul/go.mod b/contrib/registry/consul/go.mod index f746247d7..03ed997f5 100644 --- a/contrib/registry/consul/go.mod +++ b/contrib/registry/consul/go.mod @@ -3,8 +3,8 @@ module github.com/go-kratos/kratos/contrib/registry/consul/v2 go 1.16 require ( - github.com/go-kratos/kratos/v2 v2.3.1 - github.com/hashicorp/consul/api v1.12.0 + github.com/go-kratos/kratos/v2 v2.4.1 + github.com/hashicorp/consul/api v1.13.1 ) replace github.com/go-kratos/kratos/v2 => ../../../ diff --git a/contrib/registry/consul/go.sum b/contrib/registry/consul/go.sum index 402ade80b..248906563 100644 --- a/contrib/registry/consul/go.sum +++ b/contrib/registry/consul/go.sum @@ -64,15 +64,16 @@ github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o= github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= -github.com/hashicorp/consul/api v1.12.0 h1:k3y1FYv6nuKyNTqj6w9gXOx5r5CfLj/k/euUeBXj1OY= -github.com/hashicorp/consul/api v1.12.0/go.mod h1:6pVBMo0ebnYdt2S3H87XhekM/HHrUoTD2XXb/VrZVy0= -github.com/hashicorp/consul/sdk v0.8.0 h1:OJtKBtEjboEZvG6AOUdh4Z1Zbyu0WcxQ0qatRrZHTVU= -github.com/hashicorp/consul/sdk v0.8.0/go.mod h1:GBvyrGALthsZObzUGsfgHZQDXjg4lOjagTIwIR1vPms= +github.com/hashicorp/consul/api v1.13.1 h1:r5cPdVFUy+pFF7nt+0ArLD9hm+E39OewJkvNdjKXcL4= +github.com/hashicorp/consul/api v1.13.1/go.mod h1:+1VcOos0TVdQFqXxphG4zmGcwQB4KVGkp1maPqnkDpE= +github.com/hashicorp/consul/sdk v0.10.0 h1:rGLEh2AWK4K0KCMvqWAz2EYxQqgciIfMagWZ0nVe5MI= +github.com/hashicorp/consul/sdk v0.10.0/go.mod h1:yPkX5Q6CsxTFMjQQDJwzeNmUUF5NUGGbrDsv9wTb8cw= github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVoDkXMzJM= @@ -122,8 +123,6 @@ github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJys github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI= 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 h1:fzU/JVNcaqHQEcVFAKeR41fkiLdIPrefOvVG1VZ96U0= -github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= @@ -195,7 +194,6 @@ golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -222,6 +220,7 @@ golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBn golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/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-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= diff --git a/contrib/registry/consul/registry_test.go b/contrib/registry/consul/registry_test.go index e6b85e7d9..483faf1ad 100644 --- a/contrib/registry/consul/registry_test.go +++ b/contrib/registry/consul/registry_test.go @@ -5,7 +5,6 @@ import ( "fmt" "net" "reflect" - "strconv" "testing" "time" @@ -24,7 +23,123 @@ func tcpServer(t *testing.T, lis net.Listener) { } } -func TestRegister(t *testing.T) { +func TestRegistry_Register(t *testing.T) { + opts := []Option{ + WithHealthCheck(false), + } + + type args struct { + ctx context.Context + serverName string + server []*registry.ServiceInstance + } + + test := []struct { + name string + args args + want []*registry.ServiceInstance + wantErr bool + }{ + { + name: "normal", + args: args{ + ctx: context.Background(), + serverName: "server-1", + server: []*registry.ServiceInstance{ + { + ID: "1", + Name: "server-1", + Version: "v0.0.1", + Metadata: nil, + Endpoints: []string{"http://127.0.0.1:8000"}, + }, + }, + }, + want: []*registry.ServiceInstance{ + { + ID: "1", + Name: "server-1", + Version: "v0.0.1", + Metadata: nil, + Endpoints: []string{"http://127.0.0.1:8000"}, + }, + }, + wantErr: false, + }, + { + name: "registry new service replace old service", + args: args{ + ctx: context.Background(), + serverName: "server-1", + server: []*registry.ServiceInstance{ + { + ID: "1", + Name: "server-1", + Version: "v0.0.1", + Metadata: nil, + Endpoints: []string{"http://127.0.0.1:8000"}, + }, + { + ID: "1", + Name: "server-1", + Version: "v0.0.2", + Metadata: nil, + Endpoints: []string{"http://127.0.0.1:8000"}, + }, + }, + }, + want: []*registry.ServiceInstance{ + { + ID: "1", + Name: "server-1", + Version: "v0.0.2", + Metadata: nil, + Endpoints: []string{"http://127.0.0.1:8000"}, + }, + }, + wantErr: false, + }, + } + + for _, tt := range test { + t.Run(tt.name, func(t *testing.T) { + cli, err := api.NewClient(&api.Config{Address: "127.0.0.1:8500"}) + if err != nil { + t.Fatalf("create consul client failed: %v", err) + } + + r := New(cli, opts...) + + for _, instance := range tt.args.server { + err = r.Register(tt.args.ctx, instance) + if err != nil { + t.Error(err) + } + } + + watch, err := r.Watch(tt.args.ctx, tt.args.serverName) + if err != nil { + t.Error(err) + } + got, err := watch.Next() + + if (err != nil) != tt.wantErr { + t.Errorf("GetService() error = %v, wantErr %v", err, tt.wantErr) + t.Errorf("GetService() got = %v", got) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("GetService() got = %v, want %v", got, tt.want) + } + + for _, instance := range tt.args.server { + _ = r.Deregister(tt.args.ctx, instance) + } + }) + } +} + +func TestRegistry_GetService(t *testing.T) { addr := fmt.Sprintf("%s:9091", getIntranetIP()) lis, err := net.Listen("tcp", addr) if err != nil { @@ -44,47 +159,212 @@ func TestRegister(t *testing.T) { WithHealthCheckInterval(5), } r := New(cli, opts...) - if err != nil { - t.Errorf("new consul registry failed: %v", err) - } - version := strconv.FormatInt(time.Now().Unix(), 10) - svc := ®istry.ServiceInstance{ - ID: "test2233", - Name: "test-provider", - Version: version, - Metadata: map[string]string{"app": "kratos"}, + + instance1 := ®istry.ServiceInstance{ + ID: "1", + Name: "server-1", + Version: "v0.0.1", Endpoints: []string{fmt.Sprintf("tcp://%s?isSecure=false", addr)}, } - ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) - defer cancel() - err = r.Register(ctx, svc) - if err != nil { - t.Errorf("Register failed: %v", err) + + type fields struct { + registry *Registry } - w, err := r.Watch(ctx, "test-provider") - if err != nil { - t.Errorf("Watchfailed: %v", err) + type args struct { + ctx context.Context + serviceName string + } + tests := []struct { + name string + fields fields + args args + want []*registry.ServiceInstance + wantErr bool + preFunc func(t *testing.T) + deferFunc func(t *testing.T) + }{ + { + name: "normal", + fields: fields{r}, + args: args{ + ctx: context.Background(), + serviceName: "server-1", + }, + want: []*registry.ServiceInstance{instance1}, + wantErr: false, + preFunc: func(t *testing.T) { + if err := r.Register(context.Background(), instance1); err != nil { + t.Error(err) + } + watch, err := r.Watch(context.Background(), instance1.Name) + if err != nil { + t.Error(err) + } + _, err = watch.Next() + if err != nil { + t.Error(err) + } + }, + deferFunc: func(t *testing.T) { + err := r.Deregister(context.Background(), instance1) + if err != nil { + t.Error(err) + } + }, + }, + { + name: "can't get any", + fields: fields{r}, + args: args{ + ctx: context.Background(), + serviceName: "server-x", + }, + want: nil, + wantErr: true, + preFunc: func(t *testing.T) { + if err := r.Register(context.Background(), instance1); err != nil { + t.Error(err) + } + watch, err := r.Watch(context.Background(), instance1.Name) + if err != nil { + t.Error(err) + } + _, err = watch.Next() + if err != nil { + t.Error(err) + } + }, + deferFunc: func(t *testing.T) { + err := r.Deregister(context.Background(), instance1) + if err != nil { + t.Error(err) + } + }, + }, } - services, err := w.Next() - if err != nil { - t.Errorf("Next failed: %v", err) + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + if test.preFunc != nil { + test.preFunc(t) + } + if test.deferFunc != nil { + defer test.deferFunc(t) + } + + service, err := test.fields.registry.GetService(context.Background(), test.args.serviceName) + if (err != nil) != test.wantErr { + t.Errorf("GetService() error = %v, wantErr %v", err, test.wantErr) + t.Errorf("GetService() got = %v", service) + return + } + if !reflect.DeepEqual(service, test.want) { + t.Errorf("GetService() got = %v, want %v", service, test.want) + } + }) } - if !reflect.DeepEqual(1, len(services)) { - t.Errorf("no expect float_key value: %v, but got: %v", len(services), 1) +} + +func TestRegistry_Watch(t *testing.T) { + addr := fmt.Sprintf("%s:9091", getIntranetIP()) + + time.Sleep(time.Millisecond * 100) + cli, err := api.NewClient(&api.Config{Address: "127.0.0.1:8500", WaitTime: 2 * time.Second}) + if err != nil { + t.Fatalf("create consul client failed: %v", err) } - if !reflect.DeepEqual("test2233", services[0].ID) { - t.Errorf("no expect float_key value: %v, but got: %v", services[0].ID, "test2233") + + instance1 := ®istry.ServiceInstance{ + ID: "1", + Name: "server-1", + Version: "v0.0.1", + Endpoints: []string{fmt.Sprintf("tcp://%s?isSecure=false", addr)}, } - if !reflect.DeepEqual("test-provider", services[0].Name) { - t.Errorf("no expect float_key value: %v, but got: %v", services[0].Name, "test-provider") + + type args struct { + ctx context.Context + opts []Option + instance *registry.ServiceInstance } - if !reflect.DeepEqual(version, services[0].Version) { - t.Errorf("no expect float_key value: %v, but got: %v", services[0].Version, version) + tests := []struct { + name string + args args + want []*registry.ServiceInstance + wantErr bool + preFunc func(t *testing.T) + }{ + { + name: "normal", + args: args{ + ctx: context.Background(), + instance: instance1, + opts: []Option{ + WithHealthCheck(false), + }, + }, + want: []*registry.ServiceInstance{instance1}, + wantErr: false, + preFunc: func(t *testing.T) { + }, + }, + { + name: "register with healthCheck", + args: args{ + ctx: context.Background(), + instance: instance1, + opts: []Option{ + WithHeartbeat(true), + WithHealthCheck(true), + WithHealthCheckInterval(5), + }, + }, + want: []*registry.ServiceInstance{instance1}, + wantErr: false, + preFunc: func(t *testing.T) { + lis, err := net.Listen("tcp", addr) + if err != nil { + t.Errorf("listen tcp %s failed!", addr) + } + go tcpServer(t, lis) + }, + }, } - err = r.Deregister(ctx, svc) - if err != nil { - t.Errorf("Deregister failed: %v", err) + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if tt.preFunc != nil { + tt.preFunc(t) + } + + r := New(cli, tt.args.opts...) + + err := r.Register(tt.args.ctx, tt.args.instance) + if err != nil { + t.Error(err) + } + defer func() { + err = r.Deregister(tt.args.ctx, tt.args.instance) + if err != nil { + t.Error(err) + } + }() + + watch, err := r.Watch(tt.args.ctx, tt.args.instance.Name) + if err != nil { + t.Error(err) + } + + service, err := watch.Next() + + if (err != nil) != tt.wantErr { + t.Errorf("GetService() error = %v, wantErr %v", err, tt.wantErr) + t.Errorf("GetService() got = %v", service) + return + } + if !reflect.DeepEqual(service, tt.want) { + t.Errorf("GetService() got = %v, want %v", service, tt.want) + } + }) } } diff --git a/contrib/registry/discovery/go.mod b/contrib/registry/discovery/go.mod index 9b74f2658..f03c2f158 100644 --- a/contrib/registry/discovery/go.mod +++ b/contrib/registry/discovery/go.mod @@ -3,7 +3,7 @@ module github.com/go-kratos/kratos/contrib/registry/discovery/v2 go 1.16 require ( - github.com/go-kratos/kratos/v2 v2.3.1 + github.com/go-kratos/kratos/v2 v2.4.1 github.com/go-resty/resty/v2 v2.7.0 github.com/pkg/errors v0.9.1 ) diff --git a/contrib/registry/etcd/go.mod b/contrib/registry/etcd/go.mod index 04d06613b..6634eac7d 100644 --- a/contrib/registry/etcd/go.mod +++ b/contrib/registry/etcd/go.mod @@ -3,7 +3,7 @@ module github.com/go-kratos/kratos/contrib/registry/etcd/v2 go 1.16 require ( - github.com/go-kratos/kratos/v2 v2.3.1 + github.com/go-kratos/kratos/v2 v2.4.1 go.etcd.io/etcd/client/v3 v3.5.4 google.golang.org/grpc v1.46.2 ) diff --git a/contrib/registry/etcd/watcher.go b/contrib/registry/etcd/watcher.go index d04f4fbd7..22e63474c 100644 --- a/contrib/registry/etcd/watcher.go +++ b/contrib/registry/etcd/watcher.go @@ -29,7 +29,7 @@ func newWatcher(ctx context.Context, key, name string, client *clientv3.Client) serviceName: name, } w.ctx, w.cancel = context.WithCancel(ctx) - w.watchChan = w.watcher.Watch(w.ctx, key, clientv3.WithPrefix(), clientv3.WithRev(0)) + w.watchChan = w.watcher.Watch(w.ctx, key, clientv3.WithPrefix(), clientv3.WithRev(0), clientv3.WithKeysOnly()) err := w.watcher.RequestProgress(context.Background()) if err != nil { return nil, err diff --git a/contrib/registry/eureka/client.go b/contrib/registry/eureka/client.go index 5895a0576..64cdddec9 100644 --- a/contrib/registry/eureka/client.go +++ b/contrib/registry/eureka/client.go @@ -37,7 +37,7 @@ type Endpoint struct { MetaData map[string]string } -// Response for /eureka/apps +// ApplicationsRootResponse for /eureka/apps type ApplicationsRootResponse struct { ApplicationsResponse `json:"applications"` } diff --git a/contrib/registry/eureka/register.go b/contrib/registry/eureka/register.go index 31727242b..62ee81cfc 100644 --- a/contrib/registry/eureka/register.go +++ b/contrib/registry/eureka/register.go @@ -59,7 +59,7 @@ func New(eurekaUrls []string, opts ...Option) (*Registry, error) { return r, nil } -// 这里的Context是每个注册器独享的 +// Register 这里的Context是每个注册器独享的 func (r *Registry) Register(ctx context.Context, service *registry.ServiceInstance) error { return r.api.Register(ctx, service.Name, r.Endpoints(service)...) } @@ -86,7 +86,7 @@ func (r *Registry) GetService(ctx context.Context, serviceName string) ([]*regis return items, nil } -// watch 是独立的ctx +// Watch 是独立的ctx func (r *Registry) Watch(ctx context.Context, serviceName string) (registry.Watcher, error) { return newWatch(ctx, r.api, serviceName) } diff --git a/contrib/registry/kubernetes/go.mod b/contrib/registry/kubernetes/go.mod index af0841369..67dd45042 100644 --- a/contrib/registry/kubernetes/go.mod +++ b/contrib/registry/kubernetes/go.mod @@ -3,11 +3,11 @@ module github.com/go-kratos/kratos/contrib/registry/kubernetes/v2 go 1.16 require ( - github.com/go-kratos/kratos/v2 v2.3.1 + github.com/go-kratos/kratos/v2 v2.4.1 github.com/json-iterator/go v1.1.12 - k8s.io/api v0.24.1 - k8s.io/apimachinery v0.24.1 - k8s.io/client-go v0.24.1 + k8s.io/api v0.24.3 + k8s.io/apimachinery v0.24.3 + k8s.io/client-go v0.24.3 ) replace github.com/go-kratos/kratos/v2 => ../../../ diff --git a/contrib/registry/kubernetes/go.sum b/contrib/registry/kubernetes/go.sum index b24adbea1..262d31711 100644 --- a/contrib/registry/kubernetes/go.sum +++ b/contrib/registry/kubernetes/go.sum @@ -201,6 +201,7 @@ github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpO github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= +github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= @@ -652,12 +653,12 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -k8s.io/api v0.24.1 h1:BjCMRDcyEYz03joa3K1+rbshwh1Ay6oB53+iUx2H8UY= -k8s.io/api v0.24.1/go.mod h1:JhoOvNiLXKTPQ60zh2g0ewpA+bnEYf5q44Flhquh4vQ= -k8s.io/apimachinery v0.24.1 h1:ShD4aDxTQKN5zNf8K1RQ2u98ELLdIW7jEnlO9uAMX/I= -k8s.io/apimachinery v0.24.1/go.mod h1:82Bi4sCzVBdpYjyI4jY6aHX+YCUchUIrZrXKedjd2UM= -k8s.io/client-go v0.24.1 h1:w1hNdI9PFrzu3OlovVeTnf4oHDt+FJLd9Ndluvnb42E= -k8s.io/client-go v0.24.1/go.mod h1:f1kIDqcEYmwXS/vTbbhopMUbhKp2JhOeVTfxgaCIlF8= +k8s.io/api v0.24.3 h1:tt55QEmKd6L2k5DP6G/ZzdMQKvG5ro4H4teClqm0sTY= +k8s.io/api v0.24.3/go.mod h1:elGR/XSZrS7z7cSZPzVWaycpJuGIw57j9b95/1PdJNI= +k8s.io/apimachinery v0.24.3 h1:hrFiNSA2cBZqllakVYyH/VyEh4B581bQRmqATJSeQTg= +k8s.io/apimachinery v0.24.3/go.mod h1:82Bi4sCzVBdpYjyI4jY6aHX+YCUchUIrZrXKedjd2UM= +k8s.io/client-go v0.24.3 h1:Nl1840+6p4JqkFWEW2LnMKU667BUxw03REfLAVhuKQY= +k8s.io/client-go v0.24.3/go.mod h1:AAovolf5Z9bY1wIg2FZ8LPQlEdKHjLI7ZD4rw920BJw= k8s.io/gengo v0.0.0-20210813121822-485abfe95c7c/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= diff --git a/contrib/registry/kubernetes/registry.go b/contrib/registry/kubernetes/registry.go index e8aaa169e..bd752bc13 100644 --- a/contrib/registry/kubernetes/registry.go +++ b/contrib/registry/kubernetes/registry.go @@ -1,4 +1,4 @@ -// The package registry simply implements the Kubernetes-based Registry +// Package kuberegistry registry simply implements the Kubernetes-based Registry package kuberegistry import ( @@ -78,7 +78,7 @@ const ( AnnotationsKeyProtocolMap = "kratos-service-protocols" ) -// The registry simply implements service discovery based on Kubernetes +// The Registry simply implements service discovery based on Kubernetes // It has not been verified in the production environment and is currently for reference only type Registry struct { clientSet *kubernetes.Clientset @@ -156,7 +156,7 @@ func (s *Registry) Deregister(ctx context.Context, service *registry.ServiceInst }) } -// Service return the service instances in memory according to the service name. +// GetService return the service instances in memory according to the service name. func (s *Registry) GetService(ctx context.Context, name string) ([]*registry.ServiceInstance, error) { pods, err := s.podLister.List(labels.SelectorFromSet(map[string]string{ LabelsKeyServiceName: name, @@ -258,7 +258,7 @@ func GetNamespace() string { return currentNamespace } -// GetNamespace is used to get the name of the Pod where the current container is located +// GetPodName is used to get the name of the Pod where the current container is located func GetPodName() string { return os.Getenv("HOSTNAME") } diff --git a/contrib/registry/kubernetes/registry_test.go b/contrib/registry/kubernetes/registry_test.go new file mode 100644 index 000000000..2363b1c3a --- /dev/null +++ b/contrib/registry/kubernetes/registry_test.go @@ -0,0 +1,186 @@ +package kuberegistry + +import ( + "context" + "os" + "path/filepath" + "testing" + "time" + + "github.com/go-kratos/kratos/v2/registry" + + appsv1 "k8s.io/api/apps/v1" + apiv1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/rest" + "k8s.io/client-go/tools/clientcmd" + "k8s.io/client-go/util/homedir" +) + +const ( + namespace = "default" + deployName = "hello-deployment" + podName = "hello" +) + +var deployment = appsv1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Name: deployName, + }, + Spec: appsv1.DeploymentSpec{ + Replicas: int32Ptr(1), + Selector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "app": podName, + }, + }, + Template: apiv1.PodTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{ + "app": podName, + }, + }, + Spec: apiv1.PodSpec{ + Containers: []apiv1.Container{ + { + Name: "nginx", + Image: "nginx:alpine", + Ports: []apiv1.ContainerPort{ + { + Name: "http", + Protocol: apiv1.ProtocolTCP, + ContainerPort: 80, + }, + }, + Command: []string{ + "nginx", + "-g", + "daemon off;", + }, + }, + }, + }, + }, + }, +} + +func getClientSet() (*kubernetes.Clientset, error) { + restConfig, err := rest.InClusterConfig() + home := homedir.HomeDir() + + if err != nil { + kubeconfig := filepath.Join(home, ".kube", "config") + restConfig, err = clientcmd.BuildConfigFromFlags("", kubeconfig) + if err != nil { + return nil, err + } + } + clientSet, err := kubernetes.NewForConfig(restConfig) + if err != nil { + return nil, err + } + return clientSet, nil +} + +func int32Ptr(i int32) *int32 { return &i } + +func TestSetEnv(t *testing.T) { + os.Setenv("HOSTNAME", podName) + if os.Getenv("HOSTNAME") != podName { + t.Fatal("error") + } +} + +func TestRegistry(t *testing.T) { + currentNamespace = "default" + + clientSet, err := getClientSet() + if err != nil { + t.Fatal(err) + } + + r := NewRegistry(clientSet) + r.Start() + + svrHello := ®istry.ServiceInstance{ + ID: "1", + Name: "hello", + Version: "v1.0.0", + Endpoints: []string{"http://127.0.0.1:80"}, + } + _, err = clientSet.AppsV1().Deployments(namespace).Create(context.Background(), &deployment, metav1.CreateOptions{}) + + if err != nil { + t.Fatal(err) + } + + watch, err := r.Watch(context.Background(), svrHello.Name) + if err != nil { + t.Fatal(err) + } + + defer func() { + _ = watch.Stop() + }() + + go func() { + for { + res, err1 := watch.Next() + if err1 != nil { + return + } + t.Logf("watch: %d", len(res)) + for _, r := range res { + t.Logf("next: %+v", r) + } + } + }() + time.Sleep(time.Second) + + pod, err := clientSet.CoreV1().Pods(namespace).List(context.Background(), metav1.ListOptions{ + LabelSelector: "app=hello", + }) + if err != nil { + t.Fatal(err) + } + + if len(pod.Items) < 1 { + t.Fatal("fetch resource error") + } + + os.Setenv("HOSTNAME", pod.Items[0].Name) + + // Alway remember delete test resource + defer func() { + _ = clientSet.AppsV1().Deployments(namespace).Delete(context.Background(), deployName, metav1.DeleteOptions{}) + }() + + if err = r.Register(context.Background(), svrHello); err != nil { + t.Fatal(err) + } + time.Sleep(time.Second) + + res, err := r.GetService(context.Background(), svrHello.Name) + if err != nil { + t.Fatal(err) + } + + if len(res) != 1 && res[0].Name != svrHello.Name { + t.Fatal(err) + } + + if err1 := r.Deregister(context.Background(), svrHello); err1 != nil { + t.Fatal(err1) + } + + time.Sleep(time.Second) + + res, err = r.GetService(context.Background(), svrHello.Name) + if err != nil { + t.Fatal(err) + } + if len(res) != 0 { + t.Fatal("not expected empty") + } +} diff --git a/contrib/registry/nacos/go.mod b/contrib/registry/nacos/go.mod index 82d23b12f..a4665a109 100644 --- a/contrib/registry/nacos/go.mod +++ b/contrib/registry/nacos/go.mod @@ -3,7 +3,7 @@ module github.com/go-kratos/kratos/contrib/registry/nacos/v2 go 1.16 require ( - github.com/go-kratos/kratos/v2 v2.3.1 + github.com/go-kratos/kratos/v2 v2.4.1 github.com/nacos-group/nacos-sdk-go v1.0.9 ) diff --git a/contrib/registry/nacos/registry_test.go b/contrib/registry/nacos/registry_test.go index 62765b0cc..1910a68d8 100644 --- a/contrib/registry/nacos/registry_test.go +++ b/contrib/registry/nacos/registry_test.go @@ -2,8 +2,7 @@ package nacos import ( "context" - "log" - "net" + "reflect" "testing" "time" @@ -14,29 +13,9 @@ import ( "github.com/go-kratos/kratos/v2/registry" ) -func getIntranetIP() string { - addrs, err := net.InterfaceAddrs() - if err != nil { - return "127.0.0.1" - } - - for _, address := range addrs { - if ipnet, ok := address.(*net.IPNet); ok && !ipnet.IP.IsLoopback() { - if ipnet.IP.To4() != nil { - return ipnet.IP.String() - } - } - } - return "127.0.0.1" -} - -func TestRegistry(t *testing.T) { - ip := getIntranetIP() - serviceName := "golang-sms@grpc" - ctx := context.Background() - +func TestRegistry_Register(t *testing.T) { sc := []constant.ServerConfig{ - *constant.NewServerConfig(ip, 8848), + *constant.NewServerConfig("127.0.0.1", 8848), } cc := constant.ClientConfig{ @@ -60,76 +39,298 @@ func TestRegistry(t *testing.T) { if err != nil { t.Fatal(err) } + r := New(client) - _, err = client.RegisterInstance(vo.RegisterInstanceParam{ - Ip: "f", - Port: 8840, - ServiceName: serviceName, - Weight: 10, - Enable: true, - Healthy: true, - Ephemeral: true, - Metadata: map[string]string{"idc": "shanghai-xs"}, - }) - if err != nil { - t.Fatal(err) + testServer := ®istry.ServiceInstance{ + ID: "1", + Name: "test1", + Version: "v1.0.0", + Endpoints: []string{"http://127.0.0.1:8080?isSecure=false"}, + } + testServerWithMetadata := ®istry.ServiceInstance{ + ID: "1", + Name: "test1", + Version: "v1.0.0", + Endpoints: []string{"http://127.0.0.1:8080?isSecure=false"}, + Metadata: map[string]string{"idc": "shanghai-xs"}, + } + type fields struct { + registry *Registry + } + type args struct { + ctx context.Context + service *registry.ServiceInstance + } + tests := []struct { + name string + fields fields + args args + wantErr bool + deferFunc func(t *testing.T) + }{ + { + name: "normal", + fields: fields{ + registry: New(client), + }, + args: args{ + ctx: context.Background(), + service: testServer, + }, + wantErr: false, + deferFunc: func(t *testing.T) { + err = r.Deregister(context.Background(), testServer) + if err != nil { + t.Error(err) + } + }, + }, + { + name: "withMetadata", + fields: fields{ + registry: New(client), + }, + args: args{ + ctx: context.Background(), + service: testServerWithMetadata, + }, + wantErr: false, + deferFunc: func(t *testing.T) { + err = r.Deregister(context.Background(), testServerWithMetadata) + if err != nil { + t.Error(err) + } + }, + }, + { + name: "error", + fields: fields{ + registry: New(client), + }, + args: args{ + ctx: context.Background(), + service: ®istry.ServiceInstance{ + ID: "1", + Name: "", + Version: "v1.0.0", + Endpoints: []string{"http://127.0.0.1:8080?isSecure=false"}, + }, + }, + wantErr: true, + }, + { + name: "urlError", + fields: fields{ + registry: New(client), + }, + args: args{ + ctx: context.Background(), + service: ®istry.ServiceInstance{ + ID: "1", + Name: "test", + Version: "v1.0.0", + Endpoints: []string{"127.0.0.1:8080"}, + }, + }, + wantErr: true, + }, + { + name: "portError", + fields: fields{ + registry: New(client), + }, + args: args{ + ctx: context.Background(), + service: ®istry.ServiceInstance{ + ID: "1", + Name: "test", + Version: "v1.0.0", + Endpoints: []string{"http://127.0.0.1888"}, + }, + }, + wantErr: true, + }, + { + name: "withCluster", + fields: fields{ + registry: New(client, WithCluster("test")), + }, + args: args{ + ctx: context.Background(), + service: testServer, + }, + wantErr: false, + }, + { + name: "withGroup", + fields: fields{ + registry: New(client, WithGroup("TEST_GROUP")), + }, + args: args{ + ctx: context.Background(), + service: testServer, + }, + wantErr: false, + }, + { + name: "withWeight", + fields: fields{ + registry: New(client, WithWeight(200)), + }, + args: args{ + ctx: context.Background(), + service: testServer, + }, + wantErr: false, + }, + { + name: "withPrefix", + fields: fields{ + registry: New(client, WithPrefix("test")), + }, + args: args{ + ctx: context.Background(), + service: testServer, + }, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + r := tt.fields.registry + if err := r.Register(tt.args.ctx, tt.args.service); (err != nil) != tt.wantErr { + t.Errorf("Register error = %v, wantErr %v", err, tt.wantErr) + } + }) } +} - time.Sleep(time.Second) +func TestRegistry_Deregister(t *testing.T) { + testServer := ®istry.ServiceInstance{ + ID: "1", + Name: "test2", + Version: "v1.0.0", + Endpoints: []string{"http://127.0.0.1:8080?isSecure=false"}, + } - is, err := client.GetService(vo.GetServiceParam{ - ServiceName: serviceName, - }) - if err != nil { - t.Fatal(err) + type args struct { + ctx context.Context + service *registry.ServiceInstance } - t.Logf("is %#v", is) + tests := []struct { + name string + args args + wantErr bool + preFunc func(t *testing.T) + }{ + { + name: "normal", + args: args{ + ctx: context.Background(), + service: testServer, + }, + wantErr: false, + preFunc: func(t *testing.T) { + sc := []constant.ServerConfig{ + *constant.NewServerConfig("127.0.0.1", 8848), + } - time.Sleep(time.Second) - r := New(client) + cc := constant.ClientConfig{ + NamespaceId: "public", // namespace id + TimeoutMs: 5000, + NotLoadCacheAtStart: true, + LogDir: "/tmp/nacos/log", + CacheDir: "/tmp/nacos/cache", + RotateTime: "1h", + MaxAge: 3, + LogLevel: "debug", + } - go func() { - var ( - w registry.Watcher - watchErr error - ) - w, watchErr = r.Watch(ctx, "golang-sms@grpc") - if watchErr != nil { - log.Fatal(watchErr) - } - for { - var res []*registry.ServiceInstance - res, watchErr = w.Next() - if watchErr != nil { - return - } - log.Printf("watch: %d", len(res)) - for _, r := range res { - log.Printf("next: %+v", r) + // a more graceful way to create naming client + client, err := clients.NewNamingClient( + vo.NacosClientParam{ + ClientConfig: &cc, + ServerConfigs: sc, + }, + ) + if err != nil { + t.Fatal(err) + } + r := New(client) + err = r.Register(context.Background(), testServer) + if err != nil { + t.Error(err) + } + }, + }, + { + name: "error", + args: args{ + ctx: context.Background(), + service: ®istry.ServiceInstance{ + ID: "1", + Name: "test", + Version: "v1.0.0", + Endpoints: []string{"127.0.0.1:8080"}, + }, + }, + wantErr: true, + }, + { + name: "errorPort", + args: args{ + ctx: context.Background(), + service: ®istry.ServiceInstance{ + ID: "1", + Name: "notExist", + Version: "v1.0.0", + Endpoints: []string{"http://127.0.0.18080"}, + }, + }, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + sc := []constant.ServerConfig{ + *constant.NewServerConfig("127.0.0.1", 8848), } - } - }() - time.Sleep(time.Second) + cc := constant.ClientConfig{ + NamespaceId: "public", // namespace id + TimeoutMs: 5000, + NotLoadCacheAtStart: true, + LogDir: "/tmp/nacos/log", + CacheDir: "/tmp/nacos/cache", + RotateTime: "1h", + MaxAge: 3, + LogLevel: "debug", + } - ins, err := r.GetService(ctx, serviceName) - if err != nil { - t.Fatal(err) - } - for _, in := range ins { - t.Logf("ins: %#v", in) + // a more graceful way to create naming client + client, err := clients.NewNamingClient( + vo.NacosClientParam{ + ClientConfig: &cc, + ServerConfigs: sc, + }, + ) + if err != nil { + t.Fatal(err) + } + r := New(client) + if tt.preFunc != nil { + tt.preFunc(t) + } + if err := r.Deregister(tt.args.ctx, tt.args.service); (err != nil) != tt.wantErr { + t.Errorf("Deregister error = %v, wantErr %v", err, tt.wantErr) + } + }) } - - time.Sleep(time.Second) } -func TestRegistryMany(t *testing.T) { - ip := getIntranetIP() - serviceName := "golang-sms@grpc" - // ctx := context.Background() - +func TestRegistry_GetService(t *testing.T) { sc := []constant.ServerConfig{ - *constant.NewServerConfig(ip, 8848), + *constant.NewServerConfig("127.0.0.1", 8848), } cc := constant.ClientConfig{ @@ -153,61 +354,219 @@ func TestRegistryMany(t *testing.T) { if err != nil { t.Fatal(err) } - - _, err = client.RegisterInstance(vo.RegisterInstanceParam{ - Ip: "f1", - Port: 8840, - ServiceName: serviceName, - Weight: 10, - Enable: true, - Healthy: true, - Ephemeral: true, - Metadata: map[string]string{"idc": "shanghai-xs"}, - }) - if err != nil { - t.Fatal(err) + r := New(client) + testServer := ®istry.ServiceInstance{ + ID: "1", + Name: "test3", + Version: "v1.0.0", + Endpoints: []string{"grpc://127.0.0.1:8080?isSecure=false"}, } - _, err = client.RegisterInstance(vo.RegisterInstanceParam{ - Ip: "f2", - Port: 8840, - ServiceName: serviceName, - Weight: 10, - Enable: true, - Healthy: true, - Ephemeral: true, - Metadata: map[string]string{"idc": "shanghai-xs"}, - }) - if err != nil { - t.Fatal(err) + type fields struct { + registry *Registry } + type args struct { + ctx context.Context + serviceName string + } + tests := []struct { + name string + fields fields + args args + want []*registry.ServiceInstance + wantErr bool + preFunc func(t *testing.T) + deferFunc func(t *testing.T) + }{ + { + name: "normal", + preFunc: func(t *testing.T) { + err = r.Register(context.Background(), testServer) + if err != nil { + t.Error(err) + } + time.Sleep(time.Second) + }, + deferFunc: func(t *testing.T) { + err = r.Deregister(context.Background(), testServer) + if err != nil { + t.Error(err) + } + }, + fields: fields{ + registry: r, + }, + args: args{ + ctx: context.Background(), + serviceName: testServer.Name + "." + "grpc", + }, + want: []*registry.ServiceInstance{{ + ID: "127.0.0.1#8080#DEFAULT#DEFAULT_GROUP@@test3.grpc", + Name: "DEFAULT_GROUP@@test3.grpc", + Version: "v1.0.0", + Metadata: map[string]string{"version": "v1.0.0", "kind": "grpc"}, + Endpoints: []string{"grpc://127.0.0.1:8080"}, + }}, + wantErr: false, + }, + { + name: "errorNotExist", + fields: fields{ + registry: r, + }, + args: args{ + ctx: context.Background(), + serviceName: "notExist", + }, + want: nil, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if tt.preFunc != nil { + tt.preFunc(t) + } + if tt.deferFunc != nil { + defer tt.deferFunc(t) + } + r := tt.fields.registry + got, err := r.GetService(tt.args.ctx, tt.args.serviceName) + if (err != nil) != tt.wantErr { + t.Errorf("GetService error = %v, wantErr %v", err, tt.wantErr) + t.Errorf("GetService got = %v", got) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("GetService got = %v, want %v", got, tt.want) + } + }) + } +} - _, err = client.RegisterInstance(vo.RegisterInstanceParam{ - Ip: "f3", - Port: 8840, - ServiceName: serviceName, - Weight: 10, - Enable: true, - Healthy: true, - Ephemeral: true, - Metadata: map[string]string{"idc": "shanghai-xs"}, - }) - if err != nil { - t.Fatal(err) +func TestRegistry_Watch(t *testing.T) { + sc := []constant.ServerConfig{ + *constant.NewServerConfig("127.0.0.1", 8848), } - time.Sleep(time.Second) + cc := constant.ClientConfig{ + NamespaceId: "public", // namespace id + TimeoutMs: 5000, + NotLoadCacheAtStart: true, + LogDir: "/tmp/nacos/log", + CacheDir: "/tmp/nacos/cache", + RotateTime: "1h", + MaxAge: 3, + LogLevel: "debug", + } - is, err := client.GetService(vo.GetServiceParam{ - ServiceName: serviceName, - }) + // a more graceful way to create naming client + client, err := clients.NewNamingClient( + vo.NacosClientParam{ + ClientConfig: &cc, + ServerConfigs: sc, + }, + ) if err != nil { t.Fatal(err) } + r := New(client) - for _, host := range is.Hosts { - t.Logf("host: %#v,e: %v", host, err) + testServer := ®istry.ServiceInstance{ + ID: "1", + Name: "test4", + Version: "v1.0.0", + Endpoints: []string{"grpc://127.0.0.1:8080?isSecure=false"}, } - time.Sleep(time.Second) + cancelCtx, cancel := context.WithCancel(context.Background()) + type fields struct { + registry *Registry + } + type args struct { + ctx context.Context + serviceName string + } + tests := []struct { + name string + fields fields + args args + wantErr bool + want []*registry.ServiceInstance + processFunc func(t *testing.T) + }{ + { + name: "normal", + fields: fields{ + registry: New(client), + }, + args: args{ + ctx: context.Background(), + serviceName: testServer.Name + "." + "grpc", + }, + wantErr: false, + want: []*registry.ServiceInstance{{ + ID: "127.0.0.1#8080#DEFAULT#DEFAULT_GROUP@@test4.grpc", + Name: "DEFAULT_GROUP@@test4.grpc", + Version: "v1.0.0", + Metadata: map[string]string{"version": "v1.0.0", "kind": "grpc"}, + Endpoints: []string{"grpc://127.0.0.1:8080"}, + }}, + processFunc: func(t *testing.T) { + err = r.Register(context.Background(), testServer) + if err != nil { + t.Error(err) + } + }, + }, + { + name: "ctxCancel", + fields: fields{ + registry: r, + }, + args: args{ + ctx: cancelCtx, + serviceName: testServer.Name, + }, + wantErr: true, + want: nil, + processFunc: func(t *testing.T) { + cancel() + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + r := tt.fields.registry + watch, err := r.Watch(tt.args.ctx, tt.args.serviceName) + if err != nil { + t.Error(err) + return + } + defer func() { + err = watch.Stop() + if err != nil { + t.Error(err) + } + }() + _, err = watch.Next() + if err != nil { + t.Error(err) + return + } + + if tt.processFunc != nil { + tt.processFunc(t) + } + + want, err := watch.Next() + if (err != nil) != tt.wantErr { + t.Errorf("Watch error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(want, tt.want) { + t.Errorf("Watch watcher = %v, want %v", watch, tt.want) + } + }) + } } diff --git a/contrib/registry/polaris/go.mod b/contrib/registry/polaris/go.mod index 59c461c15..e399d9ddb 100644 --- a/contrib/registry/polaris/go.mod +++ b/contrib/registry/polaris/go.mod @@ -3,7 +3,7 @@ module github.com/go-kratos/kratos/contrib/registry/polaris/v2 go 1.16 require ( - github.com/go-kratos/kratos/v2 v2.3.1 + github.com/go-kratos/kratos/v2 v2.4.1 github.com/polarismesh/polaris-go v1.1.0 ) diff --git a/contrib/registry/polaris/registry_test.go b/contrib/registry/polaris/registry_test.go index d05640511..7d696064c 100644 --- a/contrib/registry/polaris/registry_test.go +++ b/contrib/registry/polaris/registry_test.go @@ -5,20 +5,26 @@ import ( "testing" "time" - "github.com/polarismesh/polaris-go/pkg/config" - - "github.com/go-kratos/kratos/v2/log" "github.com/go-kratos/kratos/v2/registry" + + "github.com/polarismesh/polaris-go/pkg/config" ) -// TestRegistry . TestRegistryManyService +// TestRegistry func TestRegistry(t *testing.T) { conf := config.NewDefaultConfiguration([]string{"127.0.0.1:8091"}) r := NewRegistryWithConfig( conf, - WithTimeout(time.Second*10), - WithTTL(100), + WithTimeout(time.Second), + WithHeartbeat(true), + WithHealthy(true), + WithIsolate(true), + WithNamespace("default"), + WithProtocol("tcp"), + WithRetryCount(0), + WithWeight(100), + WithTTL(10), ) ctx := context.Background() @@ -35,171 +41,197 @@ func TestRegistry(t *testing.T) { t.Fatal(err) } - err = r.Deregister(ctx, svc) + time.Sleep(time.Second) + + result, err := r.GetService(context.Background(), "kratos-provider-0-tcp") if err != nil { t.Fatal(err) } -} -// TestRegistryMany . TestRegistryManyService -func TestRegistryMany(t *testing.T) { - conf := config.NewDefaultConfiguration([]string{"127.0.0.1:8091"}) + if len(result) != 1 { + t.Fatal("register error") + } - r := NewRegistryWithConfig( - conf, - WithTimeout(time.Second*10), - WithTTL(100), - ) + for _, item := range result { + if item.Name != "kratos-provider-0-tcp" || item.Endpoints[0] != "tcp://127.0.0.1:9000" { + t.Fatal("register error") + } + } - svc := ®istry.ServiceInstance{ - Name: "kratos-provider-1-", - Version: "test", - Metadata: map[string]string{"app": "kratos"}, - Endpoints: []string{"tcp://127.0.0.1:9000?isSecure=false"}, + watch, err := r.Watch(ctx, "kratos-provider-0-tcp") + if err != nil { + t.Fatal(err) + } + + // Test update + svc.Version = "release1.0.0" + + if err = r.Register(ctx, svc); err != nil { + t.Fatal(err) } + + result, err = watch.Next() + + if err != nil { + t.Fatal(err) + } + + if len(result) != 1 || result[0].Version != "release1.0.0" { + t.Fatal("register error") + } + // Test add instance svc1 := ®istry.ServiceInstance{ - Name: "kratos-provider-2-", + Name: "kratos-provider-0-", Version: "test", Metadata: map[string]string{"app": "kratos"}, Endpoints: []string{"tcp://127.0.0.1:9001?isSecure=false"}, } - svc2 := ®istry.ServiceInstance{ - Name: "kratos-provider-3-", - Version: "test", - Metadata: map[string]string{"app": "kratos"}, - Endpoints: []string{"tcp://127.0.0.1:9002?isSecure=false"}, - } - err := r.Register(context.Background(), svc) - if err != nil { + if err = r.Register(ctx, svc1); err != nil { t.Fatal(err) } - err = r.Register(context.Background(), svc1) - if err != nil { + if _, err = watch.Next(); err != nil { t.Fatal(err) } - err = r.Register(context.Background(), svc2) + result, err = r.GetService(ctx, "kratos-provider-0-tcp") + if err != nil { t.Fatal(err) } - err = r.Deregister(context.Background(), svc) - if err != nil { - t.Fatal(err) + if len(result) != 2 { + t.Fatal("register error") } - err = r.Deregister(context.Background(), svc1) - if err != nil { + if err = r.Deregister(ctx, svc); err != nil { + t.Fatal(err) + } + if err = r.Deregister(ctx, svc1); err != nil { t.Fatal(err) } - err = r.Deregister(context.Background(), svc2) + result, err = watch.Next() if err != nil { t.Fatal(err) } + + if len(result) != 0 { + t.Fatal("register error") + } } -// TestGetService . TestGetService -func TestGetService(t *testing.T) { +// TestRegistryMany +func TestRegistryMany(t *testing.T) { conf := config.NewDefaultConfiguration([]string{"127.0.0.1:8091"}) r := NewRegistryWithConfig( conf, - WithTimeout(time.Second*10), - WithTTL(100), + WithTimeout(time.Second), + WithHeartbeat(true), + WithHealthy(true), + WithIsolate(true), + WithNamespace("default"), + WithProtocol("tcp"), + WithRetryCount(0), + WithWeight(100), + WithTTL(10), ) ctx := context.Background() + // Multi endpoint svc := ®istry.ServiceInstance{ - Name: "kratos-provider-4-", + Name: "kratos-provider-1-", Version: "test", Metadata: map[string]string{"app": "kratos"}, - Endpoints: []string{"tcp://127.0.0.1:9000?isSecure=false"}, + Endpoints: []string{"tcp://127.0.0.1:9000?isSecure=false", "tcp://127.0.0.1:9001?isSecure=false"}, + } + // Normal + svc1 := ®istry.ServiceInstance{ + Name: "kratos-provider-2-", + Version: "test", + Metadata: map[string]string{"app": "kratos"}, + Endpoints: []string{"tcp://127.0.0.1:9002?isSecure=false"}, + } + // Without metadata + svc2 := ®istry.ServiceInstance{ + Name: "kratos-provider-3-", + Version: "test", + Endpoints: []string{"tcp://127.0.0.1:9003?isSecure=false"}, } - err := r.Register(ctx, svc) - if err != nil { + if err := r.Register(ctx, svc); err != nil { t.Fatal(err) } - time.Sleep(time.Second * 1) - serviceInstances, err := r.GetService(ctx, "kratos-provider-4-tcp") - if err != nil { + + if err := r.Register(ctx, svc1); err != nil { t.Fatal(err) } - for _, instance := range serviceInstances { - log.Info(instance) - } - err = r.Deregister(ctx, svc) - if err != nil { + if err := r.Register(ctx, svc2); err != nil { t.Fatal(err) } -} -// TestWatch . TestWatch -func TestWatch(t *testing.T) { - conf := config.NewDefaultConfiguration([]string{"127.0.0.1:8091"}) + time.Sleep(3 * time.Second) - r := NewRegistryWithConfig( - conf, - WithTimeout(time.Second*10), - WithTTL(100), - ) + result1, err := r.GetService(ctx, "kratos-provider-1-tcp") - svc := ®istry.ServiceInstance{ - Name: "kratos-provider-4-", - Version: "test", - Metadata: map[string]string{"app": "kratos"}, - Endpoints: []string{"tcp://127.0.0.1:9000?isSecure=false"}, + if err != nil || len(result1) != 2 || result1[0].Name != "kratos-provider-1-tcp" { + t.Fatal(err) } - watch, err := r.Watch(context.Background(), "kratos-provider-4-tcp") - if err != nil { + result2, err := r.GetService(ctx, "kratos-provider-2-tcp") + + if err != nil || len(result2) != 1 || result2[0].Name != "kratos-provider-2-tcp" || result2[0].Endpoints[0] != "tcp://127.0.0.1:9002" { t.Fatal(err) } - err = r.Register(context.Background(), svc) - if err != nil { + result3, err := r.GetService(ctx, "kratos-provider-3-tcp") + + if err != nil || len(result3) != 1 || result3[0].Name != "kratos-provider-3-tcp" || result3[0].Endpoints[0] != "tcp://127.0.0.1:9003" { t.Fatal(err) } - // watch svc - time.Sleep(time.Second * 1) - // svc register, AddEvent - next, err := watch.Next() + watch1, err := r.Watch(ctx, "kratos-provider-1-tcp") if err != nil { t.Fatal(err) } - for _, instance := range next { - // it will output one instance - log.Info(instance) + watch2, err := r.Watch(ctx, "kratos-provider-2-tcp") + if err != nil { + t.Fatal(err) } - - err = r.Deregister(context.Background(), svc) + watch3, err := r.Watch(ctx, "kratos-provider-3-tcp") if err != nil { t.Fatal(err) } - // svc deregister, DeleteEvent - next, err = watch.Next() - if err != nil { + if err = r.Deregister(ctx, svc); err != nil { t.Fatal(err) } - for _, instance := range next { - // it will output nothing - log.Info(instance) + + result1, err = watch1.Next() + if err != nil || len(result1) != 0 { + t.Fatal("deregister error") } - err = watch.Stop() + err = r.Deregister(ctx, svc1) if err != nil { t.Fatal(err) } - _, err = watch.Next() - if err == nil { - // if nil, stop failed - t.Fatal() + + result2, err = watch2.Next() + if err != nil || len(result2) != 0 { + t.Fatal("deregister error") + } + err = r.Deregister(ctx, svc2) + if err != nil { + t.Fatal(err) + } + + result3, err = watch3.Next() + if err != nil || len(result3) != 0 { + t.Fatal("deregister error") } } diff --git a/contrib/registry/zookeeper/go.mod b/contrib/registry/zookeeper/go.mod index 0db3bbc3c..d8d588181 100644 --- a/contrib/registry/zookeeper/go.mod +++ b/contrib/registry/zookeeper/go.mod @@ -3,8 +3,8 @@ module github.com/go-kratos/kratos/contrib/registry/zookeeper/v2 go 1.16 require ( - github.com/go-kratos/kratos/v2 v2.3.1 - github.com/go-zookeeper/zk v1.0.2 + github.com/go-kratos/kratos/v2 v2.4.1 + github.com/go-zookeeper/zk v1.0.3 golang.org/x/sync v0.0.0-20220513210516-0976fa681c29 ) diff --git a/contrib/registry/zookeeper/go.sum b/contrib/registry/zookeeper/go.sum index c2c80c1ac..f5cce467d 100644 --- a/contrib/registry/zookeeper/go.sum +++ b/contrib/registry/zookeeper/go.sum @@ -28,8 +28,8 @@ github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/form/v4 v4.2.0/go.mod h1:q1a2BY+AQUUzhl6xA/6hBetay6dEIhMHjgvJiGo6K7U= -github.com/go-zookeeper/zk v1.0.2 h1:4mx0EYENAdX/B/rbunjlt5+4RTA/a9SMHBRuSKdGxPM= -github.com/go-zookeeper/zk v1.0.2/go.mod h1:nOB03cncLtlp4t+UAkGSV+9beXP/akpekBwL+UX1Qcw= +github.com/go-zookeeper/zk v1.0.3 h1:7M2kwOsc//9VeeFiPtf+uSJlVpU66x9Ba5+8XK7/TDg= +github.com/go-zookeeper/zk v1.0.3/go.mod h1:nOB03cncLtlp4t+UAkGSV+9beXP/akpekBwL+UX1Qcw= github.com/golang-jwt/jwt/v4 v4.4.1/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= 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= diff --git a/contrib/registry/zookeeper/register.go b/contrib/registry/zookeeper/register.go index 2aedc198a..771a5bbc4 100644 --- a/contrib/registry/zookeeper/register.go +++ b/contrib/registry/zookeeper/register.go @@ -3,6 +3,7 @@ package zookeeper import ( "context" "path" + "time" "github.com/go-zookeeper/zk" "golang.org/x/sync/singleflight" @@ -77,6 +78,7 @@ func (r *Registry) Register(ctx context.Context, service *registry.ServiceInstan if err = r.ensureName(servicePath, data, zk.FlagEphemeral); err != nil { return err } + go r.reRegister(servicePath, data) return nil } @@ -133,12 +135,20 @@ func (r *Registry) Watch(ctx context.Context, serviceName string) (registry.Watc // ensureName ensure node exists, if not exist, create and set data func (r *Registry) ensureName(path string, data []byte, flags int32) error { - exists, _, err := r.conn.Exists(path) + exists, stat, err := r.conn.Exists(path) if err != nil { return err } + // ephemeral nodes handling after restart + // fixes a race condition if the server crashes without using CreateProtectedEphemeralSequential() + if flags&zk.FlagEphemeral == zk.FlagEphemeral { + err = r.conn.Delete(path, stat.Version) + if err != nil && err != zk.ErrNoNode { + return err + } + exists = false + } if !exists { - var err error if len(r.opts.user) > 0 && len(r.opts.password) > 0 { _, err = r.conn.Create(path, data, flags, zk.DigestACL(zk.PermAll, r.opts.user, r.opts.password)) } else { @@ -150,3 +160,21 @@ func (r *Registry) ensureName(path string, data []byte, flags int32) error { } return nil } + +// reRegister re-register data node info when bad connection recovered +func (r *Registry) reRegister(path string, data []byte) { + sessionID := r.conn.SessionID() + ticker := time.NewTicker(time.Second) + defer ticker.Stop() + for range ticker.C { + cur := r.conn.SessionID() + // sessionID changed + if cur > 0 && sessionID != cur { + // re-ensureName + if err := r.ensureName(path, data, zk.FlagEphemeral); err != nil { + return + } + sessionID = cur + } + } +} diff --git a/encoding/form/form_test.go b/encoding/form/form_test.go index 11b45fe13..ddd053cda 100644 --- a/encoding/form/form_test.go +++ b/encoding/form/form_test.go @@ -204,3 +204,17 @@ func TestEncodeFieldMask(t *testing.T) { t.Log(v) } } + +func TestOptional(t *testing.T) { + v := int32(100) + req := &bdtest.HelloRequest{ + Name: "foo", + Sub: &bdtest.Sub{Name: "bar"}, + OptInt32: &v, + } + if v, _ := EncodeValues(req); v.Encode() != "name=foo&optInt32=100&sub.naming=bar" { + t.Errorf("got %s", v.Encode()) + } else { + t.Log(v) + } +} diff --git a/encoding/form/proto_encode.go b/encoding/form/proto_encode.go index 6f0a74603..449fca0de 100644 --- a/encoding/form/proto_encode.go +++ b/encoding/form/proto_encode.go @@ -26,11 +26,12 @@ func EncodeValues(msg proto.Message) (url.Values, error) { return u, nil } -func encodeByField(u url.Values, path string, v protoreflect.Message) error { - for i := 0; i < v.Descriptor().Fields().Len(); i++ { - fd := v.Descriptor().Fields().Get(i) - var key string - var newPath string +func encodeByField(u url.Values, path string, m protoreflect.Message) (finalErr error) { + m.Range(func(fd protoreflect.FieldDescriptor, v protoreflect.Value) bool { + var ( + key string + newPath string + ) if fd.HasJSONName() { key = fd.JSONName() } else { @@ -41,53 +42,55 @@ func encodeByField(u url.Values, path string, v protoreflect.Message) error { } else { newPath = path + "." + key } - if of := fd.ContainingOneof(); of != nil { - if f := v.WhichOneof(of); f != nil { + if f := m.WhichOneof(of); f != nil { if f != fd { - continue + return true } } } switch { case fd.IsList(): - if v.Get(fd).List().Len() > 0 { - list, err := encodeRepeatedField(fd, v.Get(fd).List()) + if v.List().Len() > 0 { + list, err := encodeRepeatedField(fd, v.List()) if err != nil { - return err + finalErr = err + return false } u[newPath] = list } case fd.IsMap(): - if v.Get(fd).Map().Len() > 0 { - m, err := encodeMapField(fd, v.Get(fd).Map()) + if v.Map().Len() > 0 { + m, err := encodeMapField(fd, v.Map()) if err != nil { - return err + finalErr = err + return false } for k, value := range m { u[fmt.Sprintf("%s[%s]", newPath, k)] = []string{value} } } case (fd.Kind() == protoreflect.MessageKind) || (fd.Kind() == protoreflect.GroupKind): - value, err := encodeMessage(fd.Message(), v.Get(fd)) + value, err := encodeMessage(fd.Message(), v) if err == nil { u[newPath] = []string{value} - continue + return true } - err = encodeByField(u, newPath, v.Get(fd).Message()) - if err != nil { - return err + if err = encodeByField(u, newPath, v.Message()); err != nil { + finalErr = err + return false } default: - value, err := EncodeField(fd, v.Get(fd)) + value, err := EncodeField(fd, v) if err != nil { - return err + finalErr = err + return false } u[newPath] = []string{value} } - } - - return nil + return true + }) + return } func encodeRepeatedField(fieldDescriptor protoreflect.FieldDescriptor, list protoreflect.List) ([]string, error) { @@ -99,7 +102,6 @@ func encodeRepeatedField(fieldDescriptor protoreflect.FieldDescriptor, list prot } values = append(values, value) } - return values, nil } @@ -139,7 +141,7 @@ func EncodeField(fieldDescriptor protoreflect.FieldDescriptor, value protoreflec case protoreflect.MessageKind, protoreflect.GroupKind: return encodeMessage(fieldDescriptor.Message(), value) default: - return fmt.Sprintf("%v", value.Interface()), nil + return fmt.Sprint(value.Interface()), nil } } @@ -158,7 +160,7 @@ func encodeMessage(msgDescriptor protoreflect.MessageDescriptor, value protorefl "google.protobuf.UInt64Value", "google.protobuf.UInt32Value", "google.protobuf.BoolValue", "google.protobuf.StringValue": fd := msgDescriptor.Fields() v := value.Message().Get(fd.ByName(protoreflect.Name("value"))) - return fmt.Sprintf("%v", v.Interface()), nil + return fmt.Sprint(v.Interface()), nil case fieldMaskFullName: m, ok := value.Message().Interface().(*fieldmaskpb.FieldMask) if !ok || m == nil { diff --git a/encoding/form/well_known_types_test.go b/encoding/form/well_known_types_test.go new file mode 100644 index 000000000..5598211b0 --- /dev/null +++ b/encoding/form/well_known_types_test.go @@ -0,0 +1,95 @@ +package form + +import ( + "encoding/base64" + "testing" + "time" + + "google.golang.org/protobuf/reflect/protoreflect" + "google.golang.org/protobuf/types/known/durationpb" + "google.golang.org/protobuf/types/known/timestamppb" + "google.golang.org/protobuf/types/known/wrapperspb" +) + +func TestMarshalTimeStamp(t *testing.T) { + tests := []struct { + input *timestamppb.Timestamp + expect string + }{ + { + input: timestamppb.New(time.Date(2022, 1, 2, 3, 4, 5, 6, time.UTC)), + expect: "2022-01-02T03:04:05.000000006Z", + }, + { + input: timestamppb.New(time.Date(2022, 13, 1, 13, 61, 61, 100, time.UTC)), + expect: "2023-01-01T14:02:01.000000100Z", + }, + } + for _, v := range tests { + content, err := marshalTimestamp(v.input.ProtoReflect()) + if err != nil { + t.Errorf("expect %v,got %v", nil, err) + } + if got, want := content, v.expect; got != want { + t.Errorf("expect %v,got %v", want, got) + } + } +} + +func TestMarshalDuration(t *testing.T) { + tests := []struct { + input *durationpb.Duration + expect string + }{ + { + input: durationpb.New(time.Duration(1<<63 - 1)), + expect: "2562047h47m16.854775807s", + }, + { + input: durationpb.New(time.Duration(-1 << 63)), + expect: "-2562047h47m16.854775808s", + }, + { + input: durationpb.New(100 * time.Second), + expect: "1m40s", + }, + { + input: durationpb.New(-100 * time.Second), + expect: "-1m40s", + }, + } + for _, v := range tests { + content, err := marshalDuration(v.input.ProtoReflect()) + if err != nil { + t.Errorf("expect %v,got %v", nil, err) + } + if got, want := content, v.expect; got != want { + t.Errorf("expect %v,got %v", want, got) + } + } +} + +func TestMarshalBytes(t *testing.T) { + tests := []struct { + input protoreflect.Message + expect string + }{ + { + input: wrapperspb.Bytes([]byte("abc123!?$*&()'-=@~")).ProtoReflect(), + expect: base64.StdEncoding.EncodeToString([]byte("abc123!?$*&()'-=@~")), + }, + { + input: wrapperspb.Bytes([]byte("kratos")).ProtoReflect(), + expect: base64.StdEncoding.EncodeToString([]byte("kratos")), + }, + } + for _, v := range tests { + content, err := marshalBytes(v.input) + if err != nil { + t.Errorf("expect %v,got %v", nil, err) + } + if got, want := content, v.expect; got != want { + t.Errorf("expect %v,got %v", want, got) + } + } +} diff --git a/errors/errors.go b/errors/errors.go index a18f5512e..e6bebbf8b 100644 --- a/errors/errors.go +++ b/errors/errors.go @@ -106,6 +106,9 @@ func Reason(err error) string { // Clone deep clone error to a new error. func Clone(err *Error) *Error { + if err == nil { + return nil + } metadata := make(map[string]string, len(err.Metadata)) for k, v := range err.Metadata { metadata[k] = v diff --git a/errors/errors_test.go b/errors/errors_test.go index f76d51511..5196d0b65 100644 --- a/errors/errors_test.go +++ b/errors/errors_test.go @@ -114,20 +114,37 @@ func TestCause(t *testing.T) { } func TestOther(t *testing.T) { + err := Errorf(10001, "test code 10001", "message") + // Code if !reflect.DeepEqual(Code(nil), 200) { t.Errorf("Code(nil) = %v, want %v", Code(nil), 200) } if !reflect.DeepEqual(Code(errors.New("test")), UnknownCode) { t.Errorf(`Code(errors.New("test")) = %v, want %v`, Code(nil), 200) } - if !reflect.DeepEqual(Reason(errors.New("test")), UnknownReason) { - t.Errorf(`Reason(errors.New("test")) = %v, want %v`, Reason(nil), UnknownReason) - } - err := Errorf(10001, "test code 10001", "message") if !reflect.DeepEqual(Code(err), 10001) { t.Errorf(`Code(err) = %v, want %v`, Code(err), 10001) } + // Reason + if !reflect.DeepEqual(Reason(nil), UnknownReason) { + t.Errorf(`Reason(nil) = %v, want %v`, Reason(nil), UnknownReason) + } + if !reflect.DeepEqual(Reason(errors.New("test")), UnknownReason) { + t.Errorf(`Reason(errors.New("test")) = %v, want %v`, Reason(nil), UnknownReason) + } if !reflect.DeepEqual(Reason(err), "test code 10001") { t.Errorf(`Reason(err) = %v, want %v`, Reason(err), "test code 10001") } + // Clone + err400 := Newf(http.StatusBadRequest, "BAD_REQUEST", "param invalid") + err400.Metadata = map[string]string{ + "key1": "val1", + "key2": "val2", + } + if cerr := Clone(err400); cerr == nil || cerr.Error() != err400.Error() { + t.Errorf("Clone(err) = %v, want %v", Clone(err400), err400) + } + if cerr := Clone(nil); cerr != nil { + t.Errorf("Clone(nil) = %v, want %v", Clone(err400), err400) + } } diff --git a/internal/endpoint/endpoint_test.go b/internal/endpoint/endpoint_test.go index 356d8fbcd..e8870c800 100644 --- a/internal/endpoint/endpoint_test.go +++ b/internal/endpoint/endpoint_test.go @@ -139,3 +139,87 @@ func TestParseEndpoint(t *testing.T) { }) } } + +func TestIsSecure(t *testing.T) { + tests := []struct { + url *url.URL + want bool + }{ + { + url: &url.URL{Scheme: "http", Host: "127.0.0.1"}, + want: false, + }, + { + url: &url.URL{Scheme: "http", Host: "127.0.0.1", RawQuery: "isSecure=false"}, + want: false, + }, + { + url: &url.URL{Scheme: "grpc", Host: "127.0.0.1", RawQuery: "isSecure=true"}, + want: true, + }, + } + for _, tt := range tests { + if got := IsSecure(tt.url); got != tt.want { + t.Errorf("IsSecure() = %v, want %v", got, tt.want) + } + } +} + +func TestLegacyURLToNew(t *testing.T) { + tests := []struct { + url *url.URL + want *url.URL + }{ + { + url: &url.URL{Scheme: "http", Host: "www.google.com", RawQuery: "isSecure=true"}, + want: &url.URL{Scheme: "https", Host: "www.google.com"}, + }, + { + url: &url.URL{Scheme: "https", Host: "www.google.com", RawQuery: "isSecure=true"}, + want: &url.URL{Scheme: "https", Host: "www.google.com", RawQuery: "isSecure=true"}, + }, + { + url: &url.URL{Scheme: "http", Host: "go-kratos.dev", RawQuery: "isSecure=false"}, + want: &url.URL{Scheme: "http", Host: "go-kratos.dev", RawQuery: "isSecure=false"}, + }, + } + for _, tt := range tests { + if got := legacyURLToNew(tt.url); !reflect.DeepEqual(got, tt.want) { + t.Errorf("legacyURLToNew() = %v, want %v", got, tt.want) + } + } +} + +func TestSchema(t *testing.T) { + tests := []struct { + schema string + secure bool + want string + }{ + { + schema: "http", + secure: true, + want: "https", + }, + { + schema: "http", + secure: false, + want: "http", + }, + { + schema: "grpc", + secure: true, + want: "grpcs", + }, + { + schema: "grpc", + secure: false, + want: "grpc", + }, + } + for _, tt := range tests { + if got := Scheme(tt.schema, tt.secure); got != tt.want { + t.Errorf("Schema() = %v, want %v", got, tt.want) + } + } +} diff --git a/internal/matcher/middleware.go b/internal/matcher/middleware.go new file mode 100644 index 000000000..8d5681820 --- /dev/null +++ b/internal/matcher/middleware.go @@ -0,0 +1,62 @@ +package matcher + +import ( + "sort" + "strings" + + "github.com/go-kratos/kratos/v2/middleware" +) + +// Matcher is a middleware matcher. +type Matcher interface { + Use(ms ...middleware.Middleware) + Add(selector string, ms ...middleware.Middleware) + Match(operation string) []middleware.Middleware +} + +// New new a middleware matcher. +func New() Matcher { + return &matcher{ + matchs: make(map[string][]middleware.Middleware), + } +} + +type matcher struct { + prefix []string + defaults []middleware.Middleware + matchs map[string][]middleware.Middleware +} + +func (m *matcher) Use(ms ...middleware.Middleware) { + m.defaults = ms +} + +func (m *matcher) Add(selector string, ms ...middleware.Middleware) { + if strings.HasSuffix(selector, "*") { + selector = strings.TrimSuffix(selector, "*") + m.prefix = append(m.prefix, selector) + // sort the prefix: + // - /foo/bar + // - /foo + sort.Slice(m.prefix, func(i, j int) bool { + return m.prefix[i] > m.prefix[j] + }) + } + m.matchs[selector] = ms +} + +func (m *matcher) Match(operation string) []middleware.Middleware { + ms := make([]middleware.Middleware, 0, len(m.defaults)) + if len(m.defaults) > 0 { + ms = append(ms, m.defaults...) + } + if next, ok := m.matchs[operation]; ok { + return append(ms, next...) + } + for _, prefix := range m.prefix { + if strings.HasPrefix(operation, prefix) { + return append(ms, m.matchs[prefix]...) + } + } + return ms +} diff --git a/internal/matcher/middleware_test.go b/internal/matcher/middleware_test.go new file mode 100644 index 000000000..9c948aebc --- /dev/null +++ b/internal/matcher/middleware_test.go @@ -0,0 +1,62 @@ +package matcher + +import ( + "context" + "testing" + + "github.com/go-kratos/kratos/v2/middleware" +) + +func logging(module string) middleware.Middleware { + return func(handler middleware.Handler) middleware.Handler { + return func(ctx context.Context, req interface{}) (reply interface{}, err error) { + return module, nil + } + } +} + +func equal(ms []middleware.Middleware, modules ...string) bool { + if len(ms) == 0 { + return false + } + for i, m := range ms { + x, _ := m(nil)(nil, nil) + if x != modules[i] { + return false + } + } + return true +} + +func TestMatcher(t *testing.T) { + m := New() + m.Use(logging("logging")) + m.Add("*", logging("*")) + m.Add("/foo/*", logging("foo/*")) + m.Add("/foo/bar/*", logging("foo/bar/*")) + m.Add("/foo/bar", logging("foo/bar")) + + if ms := m.Match("/"); len(ms) != 2 { + t.Fatal("not equal") + } else if !equal(ms, "logging", "*") { + t.Fatal("not equal") + } + + if ms := m.Match("/foo/xxx"); len(ms) != 2 { + t.Fatal("not equal") + } else if !equal(ms, "logging", "foo/*") { + t.Fatal("not equal") + } + + if ms := m.Match("/foo/bar"); len(ms) != 2 { + t.Fatal("not equal") + } else if !equal(ms, "logging", "foo/bar") { + t.Fatal("not equal") + } + + if ms := m.Match("/foo/bar/x"); len(ms) != 2 { + t.Fatal("not equal") + } else if !equal(ms, "logging", "foo/bar/*") { + t.Fatal("not equal") + } +} diff --git a/internal/testdata/binding/generate.go b/internal/testdata/binding/generate.go new file mode 100644 index 000000000..7abe42f8e --- /dev/null +++ b/internal/testdata/binding/generate.go @@ -0,0 +1,3 @@ +package binding + +//go:generate protoc -I . --go_out=paths=source_relative:. ./test.proto diff --git a/internal/testdata/binding/test.pb.go b/internal/testdata/binding/test.pb.go index 52d157848..a0f909199 100644 --- a/internal/testdata/binding/test.pb.go +++ b/internal/testdata/binding/test.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.28.0 -// protoc v3.20.0 +// protoc v3.17.3 // source: test.proto package binding @@ -27,9 +27,14 @@ type HelloRequest struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` - Sub *Sub `protobuf:"bytes,2,opt,name=sub,proto3" json:"sub,omitempty"` - UpdateMask *fieldmaskpb.FieldMask `protobuf:"bytes,3,opt,name=update_mask,json=updateMask,proto3" json:"update_mask,omitempty"` + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Sub *Sub `protobuf:"bytes,2,opt,name=sub,proto3" json:"sub,omitempty"` + UpdateMask *fieldmaskpb.FieldMask `protobuf:"bytes,3,opt,name=update_mask,json=updateMask,proto3" json:"update_mask,omitempty"` + OptInt32 *int32 `protobuf:"varint,4,opt,name=opt_int32,json=optInt32,proto3,oneof" json:"opt_int32,omitempty"` + OptInt64 *int64 `protobuf:"varint,5,opt,name=opt_int64,json=optInt64,proto3,oneof" json:"opt_int64,omitempty"` + OptString *string `protobuf:"bytes,6,opt,name=opt_string,json=optString,proto3,oneof" json:"opt_string,omitempty"` + SubField *Sub `protobuf:"bytes,7,opt,name=subField,proto3" json:"subField,omitempty"` + TestRepeated []string `protobuf:"bytes,8,rep,name=test_repeated,proto3" json:"test_repeated,omitempty"` } func (x *HelloRequest) Reset() { @@ -85,6 +90,41 @@ func (x *HelloRequest) GetUpdateMask() *fieldmaskpb.FieldMask { return nil } +func (x *HelloRequest) GetOptInt32() int32 { + if x != nil && x.OptInt32 != nil { + return *x.OptInt32 + } + return 0 +} + +func (x *HelloRequest) GetOptInt64() int64 { + if x != nil && x.OptInt64 != nil { + return *x.OptInt64 + } + return 0 +} + +func (x *HelloRequest) GetOptString() string { + if x != nil && x.OptString != nil { + return *x.OptString + } + return "" +} + +func (x *HelloRequest) GetSubField() *Sub { + if x != nil { + return x.SubField + } + return nil +} + +func (x *HelloRequest) GetTestRepeated() []string { + if x != nil { + return x.TestRepeated + } + return nil +} + type Sub struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -138,20 +178,35 @@ var file_test_proto_rawDesc = []byte{ 0x0a, 0x0a, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x07, 0x62, 0x69, 0x6e, 0x64, 0x69, 0x6e, 0x67, 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, 0x22, 0x7f, 0x0a, 0x0c, 0x48, 0x65, 0x6c, 0x6c, 0x6f, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1e, 0x0a, 0x03, 0x73, - 0x75, 0x62, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x62, 0x69, 0x6e, 0x64, 0x69, - 0x6e, 0x67, 0x2e, 0x53, 0x75, 0x62, 0x52, 0x03, 0x73, 0x75, 0x62, 0x12, 0x3b, 0x0a, 0x0b, 0x75, - 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x6d, 0x61, 0x73, 0x6b, 0x18, 0x03, 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, 0x22, 0x1b, 0x0a, 0x03, 0x53, 0x75, 0x62, 0x12, - 0x14, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6e, - 0x61, 0x6d, 0x69, 0x6e, 0x67, 0x42, 0x2f, 0x5a, 0x2d, 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, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x62, - 0x69, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x6b, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xe2, 0x02, 0x0a, 0x0c, 0x48, 0x65, 0x6c, 0x6c, + 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1e, 0x0a, 0x03, + 0x73, 0x75, 0x62, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x62, 0x69, 0x6e, 0x64, + 0x69, 0x6e, 0x67, 0x2e, 0x53, 0x75, 0x62, 0x52, 0x03, 0x73, 0x75, 0x62, 0x12, 0x3b, 0x0a, 0x0b, + 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x6d, 0x61, 0x73, 0x6b, 0x18, 0x03, 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, 0x12, 0x20, 0x0a, 0x09, 0x6f, 0x70, 0x74, + 0x5f, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x48, 0x00, 0x52, 0x08, + 0x6f, 0x70, 0x74, 0x49, 0x6e, 0x74, 0x33, 0x32, 0x88, 0x01, 0x01, 0x12, 0x20, 0x0a, 0x09, 0x6f, + 0x70, 0x74, 0x5f, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x48, 0x01, + 0x52, 0x08, 0x6f, 0x70, 0x74, 0x49, 0x6e, 0x74, 0x36, 0x34, 0x88, 0x01, 0x01, 0x12, 0x22, 0x0a, + 0x0a, 0x6f, 0x70, 0x74, 0x5f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x18, 0x06, 0x20, 0x01, 0x28, + 0x09, 0x48, 0x02, 0x52, 0x09, 0x6f, 0x70, 0x74, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x88, 0x01, + 0x01, 0x12, 0x28, 0x0a, 0x08, 0x73, 0x75, 0x62, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x18, 0x07, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x62, 0x69, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x2e, 0x53, 0x75, + 0x62, 0x52, 0x08, 0x73, 0x75, 0x62, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x12, 0x24, 0x0a, 0x0d, 0x74, + 0x65, 0x73, 0x74, 0x5f, 0x72, 0x65, 0x70, 0x65, 0x61, 0x74, 0x65, 0x64, 0x18, 0x08, 0x20, 0x03, + 0x28, 0x09, 0x52, 0x0d, 0x74, 0x65, 0x73, 0x74, 0x5f, 0x72, 0x65, 0x70, 0x65, 0x61, 0x74, 0x65, + 0x64, 0x42, 0x0c, 0x0a, 0x0a, 0x5f, 0x6f, 0x70, 0x74, 0x5f, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x42, + 0x0c, 0x0a, 0x0a, 0x5f, 0x6f, 0x70, 0x74, 0x5f, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x42, 0x0d, 0x0a, + 0x0b, 0x5f, 0x6f, 0x70, 0x74, 0x5f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x22, 0x1b, 0x0a, 0x03, + 0x53, 0x75, 0x62, 0x12, 0x14, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x06, 0x6e, 0x61, 0x6d, 0x69, 0x6e, 0x67, 0x42, 0x2f, 0x5a, 0x2d, 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, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, + 0x72, 0x74, 0x2f, 0x62, 0x69, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x33, } var ( @@ -175,11 +230,12 @@ var file_test_proto_goTypes = []interface{}{ var file_test_proto_depIdxs = []int32{ 1, // 0: binding.HelloRequest.sub:type_name -> binding.Sub 2, // 1: binding.HelloRequest.update_mask:type_name -> google.protobuf.FieldMask - 2, // [2:2] is the sub-list for method output_type - 2, // [2:2] is the sub-list for method input_type - 2, // [2:2] is the sub-list for extension type_name - 2, // [2:2] is the sub-list for extension extendee - 0, // [0:2] is the sub-list for field type_name + 1, // 2: binding.HelloRequest.subField:type_name -> binding.Sub + 3, // [3:3] is the sub-list for method output_type + 3, // [3:3] is the sub-list for method input_type + 3, // [3:3] is the sub-list for extension type_name + 3, // [3:3] is the sub-list for extension extendee + 0, // [0:3] is the sub-list for field type_name } func init() { file_test_proto_init() } @@ -213,6 +269,7 @@ func file_test_proto_init() { } } } + file_test_proto_msgTypes[0].OneofWrappers = []interface{}{} type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ diff --git a/internal/testdata/binding/test.proto b/internal/testdata/binding/test.proto index 06bd2f994..f6df42fc4 100644 --- a/internal/testdata/binding/test.proto +++ b/internal/testdata/binding/test.proto @@ -9,8 +9,13 @@ option go_package = "github.com/go-kratos/kratos/transport/binding"; // The request message containing the user's name. message HelloRequest { string name = 1; - Sub sub = 2; + Sub sub = 2; google.protobuf.FieldMask update_mask = 3; + optional int32 opt_int32 = 4; + optional int64 opt_int64 = 5; + optional string opt_string = 6; + Sub subField = 7; + repeated string test_repeated = 8 [json_name = "test_repeated"]; } message Sub{ diff --git a/internal/testdata/helloworld/generate.go b/internal/testdata/helloworld/generate.go new file mode 100644 index 000000000..05de6fcb3 --- /dev/null +++ b/internal/testdata/helloworld/generate.go @@ -0,0 +1,3 @@ +package helloworld + +//go:generate protoc -I . -I ../../../third_party --go_out=paths=source_relative:. --go-grpc_out=paths=source_relative:. --go-http_out=paths=source_relative:. ./helloworld.proto diff --git a/internal/testdata/helloworld/helloworld.pb.go b/internal/testdata/helloworld/helloworld.pb.go index 656819d43..48d44179e 100644 --- a/internal/testdata/helloworld/helloworld.pb.go +++ b/internal/testdata/helloworld/helloworld.pb.go @@ -1,8 +1,8 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.27.1 +// protoc-gen-go v1.28.0 // protoc v3.17.3 -// source: helloworld/helloworld.proto +// source: helloworld.proto package helloworld @@ -33,7 +33,7 @@ type HelloRequest struct { func (x *HelloRequest) Reset() { *x = HelloRequest{} if protoimpl.UnsafeEnabled { - mi := &file_helloworld_helloworld_proto_msgTypes[0] + mi := &file_helloworld_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -46,7 +46,7 @@ func (x *HelloRequest) String() string { func (*HelloRequest) ProtoMessage() {} func (x *HelloRequest) ProtoReflect() protoreflect.Message { - mi := &file_helloworld_helloworld_proto_msgTypes[0] + mi := &file_helloworld_proto_msgTypes[0] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -59,7 +59,7 @@ func (x *HelloRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use HelloRequest.ProtoReflect.Descriptor instead. func (*HelloRequest) Descriptor() ([]byte, []int) { - return file_helloworld_helloworld_proto_rawDescGZIP(), []int{0} + return file_helloworld_proto_rawDescGZIP(), []int{0} } func (x *HelloRequest) GetName() string { @@ -81,7 +81,7 @@ type HelloReply struct { func (x *HelloReply) Reset() { *x = HelloReply{} if protoimpl.UnsafeEnabled { - mi := &file_helloworld_helloworld_proto_msgTypes[1] + mi := &file_helloworld_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -94,7 +94,7 @@ func (x *HelloReply) String() string { func (*HelloReply) ProtoMessage() {} func (x *HelloReply) ProtoReflect() protoreflect.Message { - mi := &file_helloworld_helloworld_proto_msgTypes[1] + mi := &file_helloworld_proto_msgTypes[1] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -107,7 +107,7 @@ func (x *HelloReply) ProtoReflect() protoreflect.Message { // Deprecated: Use HelloReply.ProtoReflect.Descriptor instead. func (*HelloReply) Descriptor() ([]byte, []int) { - return file_helloworld_helloworld_proto_rawDescGZIP(), []int{1} + return file_helloworld_proto_rawDescGZIP(), []int{1} } func (x *HelloReply) GetMessage() string { @@ -117,65 +117,71 @@ func (x *HelloReply) GetMessage() string { return "" } -var File_helloworld_helloworld_proto protoreflect.FileDescriptor - -var file_helloworld_helloworld_proto_rawDesc = []byte{ - 0x0a, 0x1b, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x2f, 0x68, 0x65, 0x6c, - 0x6c, 0x6f, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0a, 0x68, - 0x65, 0x6c, 0x6c, 0x6f, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x1a, 0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, - 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x22, 0x0a, 0x0c, 0x48, 0x65, 0x6c, 0x6c, 0x6f, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x26, 0x0a, 0x0a, 0x48, - 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, - 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, - 0x61, 0x67, 0x65, 0x32, 0x63, 0x0a, 0x07, 0x47, 0x72, 0x65, 0x65, 0x74, 0x65, 0x72, 0x12, 0x58, - 0x0a, 0x08, 0x53, 0x61, 0x79, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x12, 0x18, 0x2e, 0x68, 0x65, 0x6c, - 0x6c, 0x6f, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x2e, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x77, 0x6f, 0x72, 0x6c, - 0x64, 0x2e, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x1a, 0x82, 0xd3, - 0xe4, 0x93, 0x02, 0x14, 0x12, 0x12, 0x2f, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x77, 0x6f, 0x72, 0x6c, - 0x64, 0x2f, 0x7b, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x42, 0x3d, 0x5a, 0x3b, 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, 0x69, 0x6e, 0x74, 0x65, 0x72, - 0x6e, 0x61, 0x6c, 0x2f, 0x74, 0x65, 0x73, 0x74, 0x64, 0x61, 0x74, 0x61, 0x2f, 0x68, 0x65, 0x6c, - 0x6c, 0x6f, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +var File_helloworld_proto protoreflect.FileDescriptor + +var file_helloworld_proto_rawDesc = []byte{ + 0x0a, 0x10, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x12, 0x0a, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x1a, 0x1c, + 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x22, 0x0a, 0x0c, + 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, + 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, + 0x22, 0x26, 0x0a, 0x0a, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x18, + 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x32, 0xab, 0x01, 0x0a, 0x07, 0x47, 0x72, 0x65, + 0x65, 0x74, 0x65, 0x72, 0x12, 0x58, 0x0a, 0x08, 0x53, 0x61, 0x79, 0x48, 0x65, 0x6c, 0x6c, 0x6f, + 0x12, 0x18, 0x2e, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x2e, 0x48, 0x65, + 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x68, 0x65, 0x6c, + 0x6c, 0x6f, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x2e, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x70, + 0x6c, 0x79, 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x12, 0x12, 0x2f, 0x68, 0x65, 0x6c, + 0x6c, 0x6f, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x2f, 0x7b, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x12, 0x46, + 0x0a, 0x0e, 0x53, 0x61, 0x79, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, + 0x12, 0x18, 0x2e, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x2e, 0x48, 0x65, + 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x68, 0x65, 0x6c, + 0x6c, 0x6f, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x2e, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x70, + 0x6c, 0x79, 0x28, 0x01, 0x30, 0x01, 0x42, 0x3d, 0x5a, 0x3b, 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, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, + 0x6c, 0x2f, 0x74, 0x65, 0x73, 0x74, 0x64, 0x61, 0x74, 0x61, 0x2f, 0x68, 0x65, 0x6c, 0x6c, 0x6f, + 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( - file_helloworld_helloworld_proto_rawDescOnce sync.Once - file_helloworld_helloworld_proto_rawDescData = file_helloworld_helloworld_proto_rawDesc + file_helloworld_proto_rawDescOnce sync.Once + file_helloworld_proto_rawDescData = file_helloworld_proto_rawDesc ) -func file_helloworld_helloworld_proto_rawDescGZIP() []byte { - file_helloworld_helloworld_proto_rawDescOnce.Do(func() { - file_helloworld_helloworld_proto_rawDescData = protoimpl.X.CompressGZIP(file_helloworld_helloworld_proto_rawDescData) +func file_helloworld_proto_rawDescGZIP() []byte { + file_helloworld_proto_rawDescOnce.Do(func() { + file_helloworld_proto_rawDescData = protoimpl.X.CompressGZIP(file_helloworld_proto_rawDescData) }) - return file_helloworld_helloworld_proto_rawDescData + return file_helloworld_proto_rawDescData } -var file_helloworld_helloworld_proto_msgTypes = make([]protoimpl.MessageInfo, 2) -var file_helloworld_helloworld_proto_goTypes = []interface{}{ +var file_helloworld_proto_msgTypes = make([]protoimpl.MessageInfo, 2) +var file_helloworld_proto_goTypes = []interface{}{ (*HelloRequest)(nil), // 0: helloworld.HelloRequest (*HelloReply)(nil), // 1: helloworld.HelloReply } -var file_helloworld_helloworld_proto_depIdxs = []int32{ +var file_helloworld_proto_depIdxs = []int32{ 0, // 0: helloworld.Greeter.SayHello:input_type -> helloworld.HelloRequest - 1, // 1: helloworld.Greeter.SayHello:output_type -> helloworld.HelloReply - 1, // [1:2] is the sub-list for method output_type - 0, // [0:1] is the sub-list for method input_type + 0, // 1: helloworld.Greeter.SayHelloStream:input_type -> helloworld.HelloRequest + 1, // 2: helloworld.Greeter.SayHello:output_type -> helloworld.HelloReply + 1, // 3: helloworld.Greeter.SayHelloStream:output_type -> helloworld.HelloReply + 2, // [2:4] is the sub-list for method output_type + 0, // [0:2] is the sub-list for method input_type 0, // [0:0] is the sub-list for extension type_name 0, // [0:0] is the sub-list for extension extendee 0, // [0:0] is the sub-list for field type_name } -func init() { file_helloworld_helloworld_proto_init() } -func file_helloworld_helloworld_proto_init() { - if File_helloworld_helloworld_proto != nil { +func init() { file_helloworld_proto_init() } +func file_helloworld_proto_init() { + if File_helloworld_proto != nil { return } if !protoimpl.UnsafeEnabled { - file_helloworld_helloworld_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + file_helloworld_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*HelloRequest); i { case 0: return &v.state @@ -187,7 +193,7 @@ func file_helloworld_helloworld_proto_init() { return nil } } - file_helloworld_helloworld_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + file_helloworld_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*HelloReply); i { case 0: return &v.state @@ -204,18 +210,18 @@ func file_helloworld_helloworld_proto_init() { out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: file_helloworld_helloworld_proto_rawDesc, + RawDescriptor: file_helloworld_proto_rawDesc, NumEnums: 0, NumMessages: 2, NumExtensions: 0, NumServices: 1, }, - GoTypes: file_helloworld_helloworld_proto_goTypes, - DependencyIndexes: file_helloworld_helloworld_proto_depIdxs, - MessageInfos: file_helloworld_helloworld_proto_msgTypes, + GoTypes: file_helloworld_proto_goTypes, + DependencyIndexes: file_helloworld_proto_depIdxs, + MessageInfos: file_helloworld_proto_msgTypes, }.Build() - File_helloworld_helloworld_proto = out.File - file_helloworld_helloworld_proto_rawDesc = nil - file_helloworld_helloworld_proto_goTypes = nil - file_helloworld_helloworld_proto_depIdxs = nil + File_helloworld_proto = out.File + file_helloworld_proto_rawDesc = nil + file_helloworld_proto_goTypes = nil + file_helloworld_proto_depIdxs = nil } diff --git a/internal/testdata/helloworld/helloworld.proto b/internal/testdata/helloworld/helloworld.proto index bdac62c1f..ed068a38b 100644 --- a/internal/testdata/helloworld/helloworld.proto +++ b/internal/testdata/helloworld/helloworld.proto @@ -14,6 +14,8 @@ service Greeter { get: "/helloworld/{name}", }; } + // Sends a greeting + rpc SayHelloStream (stream HelloRequest) returns (stream HelloReply); } // The request message containing the user's name. diff --git a/internal/testdata/helloworld/helloworld_grpc.pb.go b/internal/testdata/helloworld/helloworld_grpc.pb.go index 4e1c32b3d..3558cbe41 100644 --- a/internal/testdata/helloworld/helloworld_grpc.pb.go +++ b/internal/testdata/helloworld/helloworld_grpc.pb.go @@ -1,4 +1,8 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. +// versions: +// - protoc-gen-go-grpc v1.2.0 +// - protoc v3.17.3 +// source: helloworld.proto package helloworld @@ -20,6 +24,8 @@ const _ = grpc.SupportPackageIsVersion7 type GreeterClient interface { // Sends a greeting SayHello(ctx context.Context, in *HelloRequest, opts ...grpc.CallOption) (*HelloReply, error) + // Sends a greeting + SayHelloStream(ctx context.Context, opts ...grpc.CallOption) (Greeter_SayHelloStreamClient, error) } type greeterClient struct { @@ -39,12 +45,45 @@ func (c *greeterClient) SayHello(ctx context.Context, in *HelloRequest, opts ... return out, nil } +func (c *greeterClient) SayHelloStream(ctx context.Context, opts ...grpc.CallOption) (Greeter_SayHelloStreamClient, error) { + stream, err := c.cc.NewStream(ctx, &Greeter_ServiceDesc.Streams[0], "/helloworld.Greeter/SayHelloStream", opts...) + if err != nil { + return nil, err + } + x := &greeterSayHelloStreamClient{stream} + return x, nil +} + +type Greeter_SayHelloStreamClient interface { + Send(*HelloRequest) error + Recv() (*HelloReply, error) + grpc.ClientStream +} + +type greeterSayHelloStreamClient struct { + grpc.ClientStream +} + +func (x *greeterSayHelloStreamClient) Send(m *HelloRequest) error { + return x.ClientStream.SendMsg(m) +} + +func (x *greeterSayHelloStreamClient) Recv() (*HelloReply, error) { + m := new(HelloReply) + if err := x.ClientStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + // GreeterServer is the server API for Greeter service. // All implementations must embed UnimplementedGreeterServer // for forward compatibility type GreeterServer interface { // Sends a greeting SayHello(context.Context, *HelloRequest) (*HelloReply, error) + // Sends a greeting + SayHelloStream(Greeter_SayHelloStreamServer) error mustEmbedUnimplementedGreeterServer() } @@ -55,6 +94,9 @@ type UnimplementedGreeterServer struct { func (UnimplementedGreeterServer) SayHello(context.Context, *HelloRequest) (*HelloReply, error) { return nil, status.Errorf(codes.Unimplemented, "method SayHello not implemented") } +func (UnimplementedGreeterServer) SayHelloStream(Greeter_SayHelloStreamServer) error { + return status.Errorf(codes.Unimplemented, "method SayHelloStream not implemented") +} func (UnimplementedGreeterServer) mustEmbedUnimplementedGreeterServer() {} // UnsafeGreeterServer may be embedded to opt out of forward compatibility for this service. @@ -86,6 +128,32 @@ func _Greeter_SayHello_Handler(srv interface{}, ctx context.Context, dec func(in return interceptor(ctx, in, info, handler) } +func _Greeter_SayHelloStream_Handler(srv interface{}, stream grpc.ServerStream) error { + return srv.(GreeterServer).SayHelloStream(&greeterSayHelloStreamServer{stream}) +} + +type Greeter_SayHelloStreamServer interface { + Send(*HelloReply) error + Recv() (*HelloRequest, error) + grpc.ServerStream +} + +type greeterSayHelloStreamServer struct { + grpc.ServerStream +} + +func (x *greeterSayHelloStreamServer) Send(m *HelloReply) error { + return x.ServerStream.SendMsg(m) +} + +func (x *greeterSayHelloStreamServer) Recv() (*HelloRequest, error) { + m := new(HelloRequest) + if err := x.ServerStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + // Greeter_ServiceDesc is the grpc.ServiceDesc for Greeter service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) @@ -98,6 +166,13 @@ var Greeter_ServiceDesc = grpc.ServiceDesc{ Handler: _Greeter_SayHello_Handler, }, }, - Streams: []grpc.StreamDesc{}, - Metadata: "helloworld/helloworld.proto", + Streams: []grpc.StreamDesc{ + { + StreamName: "SayHelloStream", + Handler: _Greeter_SayHelloStream_Handler, + ServerStreams: true, + ClientStreams: true, + }, + }, + Metadata: "helloworld.proto", } diff --git a/internal/testdata/helloworld/helloworld_http.pb.go b/internal/testdata/helloworld/helloworld_http.pb.go index 368b57186..b77f02ec2 100644 --- a/internal/testdata/helloworld/helloworld_http.pb.go +++ b/internal/testdata/helloworld/helloworld_http.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go-http. DO NOT EDIT. // versions: -// protoc-gen-go-http v2.1.0 +// protoc-gen-go-http v2.3.1 package helloworld @@ -17,6 +17,8 @@ var _ = binding.EncodeURL const _ = http.SupportPackageIsVersion1 +const OperationGreeterSayHello = "/helloworld.Greeter/SayHello" + type GreeterHTTPServer interface { SayHello(context.Context, *HelloRequest) (*HelloReply, error) } @@ -35,7 +37,7 @@ func _Greeter_SayHello0_HTTP_Handler(srv GreeterHTTPServer) func(ctx http.Contex if err := ctx.BindVars(&in); err != nil { return err } - http.SetOperation(ctx, "/helloworld.Greeter/SayHello") + http.SetOperation(ctx, OperationGreeterSayHello) h := ctx.Middleware(func(ctx context.Context, req interface{}) (interface{}, error) { return srv.SayHello(ctx, req.(*HelloRequest)) }) @@ -64,7 +66,7 @@ func (c *GreeterHTTPClientImpl) SayHello(ctx context.Context, in *HelloRequest, var out HelloReply pattern := "/helloworld/{name}" path := binding.EncodeURL(pattern, in, true) - opts = append(opts, http.Operation("/helloworld.Greeter/SayHello")) + opts = append(opts, http.Operation(OperationGreeterSayHello)) opts = append(opts, http.PathTemplate(pattern)) err := c.cc.Invoke(ctx, "GET", path, nil, &out, opts...) if err != nil { diff --git a/log/README.md b/log/README.md index 0fb786f1e..8c4c7b76f 100644 --- a/log/README.md +++ b/log/README.md @@ -42,6 +42,11 @@ log.Error("warn log") ```shell go get -u github.com/go-kratos/kratos/contrib/log/zap/v2 ``` +### logrus + +```shell +go get -u github.com/go-kratos/kratos/contrib/log/logrus/v2 +``` ### fluent diff --git a/log/global_test.go b/log/global_test.go index 914823310..04bb19c51 100644 --- a/log/global_test.go +++ b/log/global_test.go @@ -11,7 +11,12 @@ import ( func TestGlobalLog(t *testing.T) { buffer := &bytes.Buffer{} - SetLogger(NewStdLogger(buffer)) + logger := NewStdLogger(buffer) + SetLogger(logger) + + if GetLogger() != logger { + t.Error("GetLogger() is not equal to logger") + } testCases := []struct { level Level @@ -48,19 +53,38 @@ func TestGlobalLog(t *testing.T) { msg := fmt.Sprintf(tc.content[0].(string), tc.content[1:]...) switch tc.level { case LevelDebug: + Debug(msg) + expected = append(expected, fmt.Sprintf("%s msg=%s", "DEBUG", msg)) Debugf(tc.content[0].(string), tc.content[1:]...) expected = append(expected, fmt.Sprintf("%s msg=%s", "DEBUG", msg)) + Debugw("log", msg) + expected = append(expected, fmt.Sprintf("%s log=%s", "DEBUG", msg)) case LevelInfo: + Info(msg) + expected = append(expected, fmt.Sprintf("%s msg=%s", "INFO", msg)) Infof(tc.content[0].(string), tc.content[1:]...) expected = append(expected, fmt.Sprintf("%s msg=%s", "INFO", msg)) + Infow("log", msg) + expected = append(expected, fmt.Sprintf("%s log=%s", "INFO", msg)) case LevelWarn: + Warn(msg) + expected = append(expected, fmt.Sprintf("%s msg=%s", "WARN", msg)) Warnf(tc.content[0].(string), tc.content[1:]...) expected = append(expected, fmt.Sprintf("%s msg=%s", "WARN", msg)) + Warnw("log", msg) + expected = append(expected, fmt.Sprintf("%s log=%s", "WARN", msg)) case LevelError: + Error(msg) + expected = append(expected, fmt.Sprintf("%s msg=%s", "ERROR", msg)) Errorf(tc.content[0].(string), tc.content[1:]...) expected = append(expected, fmt.Sprintf("%s msg=%s", "ERROR", msg)) + Errorw("log", msg) + expected = append(expected, fmt.Sprintf("%s log=%s", "ERROR", msg)) } } + Log(LevelInfo, DefaultMessageKey, "test log") + expected = append(expected, fmt.Sprintf("%s msg=%s", "INFO", "test log")) + expected = append(expected, "") t.Logf("Content: %s", buffer.String()) @@ -85,7 +109,7 @@ func TestGlobalLogUpdate(t *testing.T) { } } -func TestGolbalContext(t *testing.T) { +func TestGlobalContext(t *testing.T) { buffer := &bytes.Buffer{} SetLogger(NewStdLogger(buffer)) Context(context.Background()).Infof("111") diff --git a/log/log_test.go b/log/log_test.go index 542bb79d1..38417f76d 100644 --- a/log/log_test.go +++ b/log/log_test.go @@ -7,7 +7,8 @@ import ( func TestInfo(t *testing.T) { logger := DefaultLogger - logger = With(logger, "ts", DefaultTimestamp, "caller", DefaultCaller) + logger = With(logger, "ts", DefaultTimestamp) + logger = With(logger, "caller", DefaultCaller) _ = logger.Log(LevelInfo, "key1", "value1") } diff --git a/metadata/metadata_test.go b/metadata/metadata_test.go index b7f7173b0..8653bbe55 100644 --- a/metadata/metadata_test.go +++ b/metadata/metadata_test.go @@ -90,6 +90,12 @@ func TestMetadata_Set(t *testing.T) { args: args{key: "env", value: "pro"}, want: Metadata{"hello": "kratos", "env": "pro"}, }, + { + name: "empty", + m: Metadata{}, + args: args{key: "", value: ""}, + want: Metadata{}, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -203,6 +209,25 @@ func TestAppendToClientContext(t *testing.T) { } } +// nolint directives: sa5012 +func TestAppendToClientContextThatPanics(t *testing.T) { + kvs := []string{"hello", "kratos", "env"} + defer func() { + if r := recover(); r == nil { + t.Errorf("append to client context singular kvs did not panic") + } + }() + ctx := NewClientContext(context.Background(), Metadata{}) + ctx = AppendToClientContext(ctx, kvs...) + md, ok := FromClientContext(ctx) + if !ok { + t.Errorf("FromServerContext() = %v, want %v", ok, true) + } + if !reflect.DeepEqual(md, Metadata{}) { + t.Errorf("metadata = %v, want %v", md, Metadata{}) + } +} + func TestMergeToClientContext(t *testing.T) { type args struct { md Metadata @@ -249,7 +274,14 @@ func TestMetadata_Range(t *testing.T) { return true }) if !reflect.DeepEqual(tmp, Metadata{"https://go-kratos.dev/": "https://go-kratos.dev/", "kratos": "kratos"}) { - t.Errorf("metadata = %v, want %v", tmp, Metadata{"kratos": "kratos"}) + t.Errorf("metadata = %v, want %v", tmp, Metadata{"https://go-kratos.dev/": "https://go-kratos.dev/", "kratos": "kratos"}) + } + tmp = Metadata{} + md.Range(func(k, v string) bool { + return false + }) + if !reflect.DeepEqual(tmp, Metadata{}) { + t.Errorf("metadata = %v, want %v", tmp, Metadata{}) } } diff --git a/middleware/circuitbreaker/circuitbreaker_test.go b/middleware/circuitbreaker/circuitbreaker_test.go new file mode 100644 index 000000000..b7c2553ce --- /dev/null +++ b/middleware/circuitbreaker/circuitbreaker_test.go @@ -0,0 +1,79 @@ +package circuitbreaker + +import ( + "context" + "errors" + "testing" + + kratos_errors "github.com/go-kratos/kratos/v2/errors" + "github.com/go-kratos/kratos/v2/internal/group" + "github.com/go-kratos/kratos/v2/transport" +) + +type transportMock struct { + kind transport.Kind + endpoint string + operation string +} + +type circuitBreakerMock struct { + err error +} + +func (tr *transportMock) Kind() transport.Kind { + return tr.kind +} + +func (tr *transportMock) Endpoint() string { + return tr.endpoint +} + +func (tr *transportMock) Operation() string { + return tr.operation +} + +func (tr *transportMock) RequestHeader() transport.Header { + return nil +} + +func (tr *transportMock) ReplyHeader() transport.Header { + return nil +} + +func (c *circuitBreakerMock) Allow() error { return c.err } +func (c *circuitBreakerMock) MarkSuccess() {} +func (c *circuitBreakerMock) MarkFailed() {} + +func Test_WithGroup(t *testing.T) { + o := options{ + group: group.NewGroup(func() interface{} { + return "" + }), + } + + WithGroup(nil)(&o) + if o.group != nil { + t.Error("The group property must be updated to nil.") + } +} + +func Test_Server(t *testing.T) { + nextValid := func(ctx context.Context, req interface{}) (interface{}, error) { + return "Hello valid", nil + } + nextInvalid := func(ctx context.Context, req interface{}) (interface{}, error) { + return nil, kratos_errors.InternalServer("", "") + } + + ctx := transport.NewClientContext(context.Background(), &transportMock{}) + + _, _ = Client(func(o *options) { + o.group = group.NewGroup(func() interface{} { + return &circuitBreakerMock{err: errors.New("circuitbreaker error")} + }) + })(nextValid)(ctx, nil) + + _, _ = Client(func(_ *options) {})(nextValid)(ctx, nil) + + _, _ = Client(func(_ *options) {})(nextInvalid)(ctx, nil) +} diff --git a/middleware/logging/logging_test.go b/middleware/logging/logging_test.go index 0427d11e8..7242f8e04 100644 --- a/middleware/logging/logging_test.go +++ b/middleware/logging/logging_test.go @@ -97,3 +97,26 @@ func TestHTTP(t *testing.T) { }) } } + +type ( + dummy struct { + field string + } + dummyStringer struct { + field string + } +) + +func (d *dummyStringer) String() string { + return "my value" +} + +func Test_extractArgs(t *testing.T) { + if extractArgs(&dummyStringer{field: ""}) != "my value" { + t.Errorf(`The stringified dummyStringer structure must be equal to "my value", %v given`, extractArgs(&dummyStringer{field: ""})) + } + + if extractArgs(&dummy{field: "value"}) != "&{field:value}" { + t.Errorf(`The stringified dummy structure must be equal to "&{field:value}", %v given`, extractArgs(&dummy{field: "value"})) + } +} diff --git a/middleware/metadata/metadata_test.go b/middleware/metadata/metadata_test.go index c286eedbd..d0190b672 100644 --- a/middleware/metadata/metadata_test.go +++ b/middleware/metadata/metadata_test.go @@ -126,3 +126,14 @@ func TestClient(t *testing.T) { t.Fatalf("want foo got %v", reply) } } + +func Test_WithPropagatedPrefix(t *testing.T) { + o := &options{ + prefix: []string{"override"}, + } + WithPropagatedPrefix("something", "another")(o) + + if len(o.prefix) != 2 { + t.Error("The prefix must be overrided.") + } +} diff --git a/middleware/metrics/metrics_test.go b/middleware/metrics/metrics_test.go index c695f2757..9cf261cee 100644 --- a/middleware/metrics/metrics_test.go +++ b/middleware/metrics/metrics_test.go @@ -2,20 +2,155 @@ package metrics import ( "context" + "errors" "testing" + + "github.com/go-kratos/kratos/v2/metrics" + "github.com/go-kratos/kratos/v2/transport" + "github.com/go-kratos/kratos/v2/transport/http" ) -func TestMetrics(t *testing.T) { - next := func(ctx context.Context, req interface{}) (interface{}, error) { - return req.(string) + "https://go-kratos.dev", nil +type ( + mockCounter struct { + lvs []string + value float64 + } + mockObserver struct { + lvs []string + value float64 } - _, err := Server()(next)(context.Background(), "test:") +) + +func (m *mockCounter) With(lvs ...string) metrics.Counter { + return m +} + +func (m *mockCounter) Inc() { + m.value += 1.0 +} + +func (m *mockCounter) Add(delta float64) { + m.value += delta +} + +func (m *mockObserver) With(lvs ...string) metrics.Observer { + return m +} + +func (m *mockObserver) Observe(delta float64) { + m.value += delta +} + +func TestWithRequests(t *testing.T) { + mock := mockCounter{ + lvs: []string{"Initial"}, + value: 1.23, + } + o := options{ + requests: &mock, + } + + WithRequests(&mock)(&o) + + if _, ok := o.requests.(*mockCounter); !ok { + t.Errorf(`The type of the option requests property must be of "mockCounter", %T given.`, o.requests) + } + + counter := o.requests.(*mockCounter) + + if len(counter.lvs) != 1 || counter.lvs[0] != "Initial" { + t.Errorf(`The given counter lvs must have only one element equal to "Initial", %v given`, counter.lvs) + } + if counter.value != 1.23 { + t.Errorf(`The given counter value must be equal to 1.23, %v given`, counter.value) + } +} + +func TestWithSeconds(t *testing.T) { + mock := mockObserver{ + lvs: []string{"Initial"}, + value: 1.23, + } + o := options{ + seconds: &mock, + } + + WithSeconds(&mock)(&o) + + if _, ok := o.seconds.(*mockObserver); !ok { + t.Errorf(`The type of the option requests property must be of "mockObserver", %T given.`, o.requests) + } + + observer := o.seconds.(*mockObserver) + + if len(observer.lvs) != 1 || observer.lvs[0] != "Initial" { + t.Errorf(`The given observer lvs must have only one element equal to "Initial", %v given`, observer.lvs) + } + if observer.value != 1.23 { + t.Errorf(`The given observer value must be equal to 1.23, %v given`, observer.value) + } +} + +func TestServer(t *testing.T) { + e := errors.New("got an error") + nextError := func(ctx context.Context, req interface{}) (interface{}, error) { + return nil, e + } + nextValid := func(ctx context.Context, req interface{}) (interface{}, error) { + return "Hello valid", nil + } + + _, err := Server()(nextError)(context.Background(), "test:") + if err != e { + t.Error("The given error mismatch the expected.") + } + + res, err := Server(func(o *options) { + o.requests = &mockCounter{ + lvs: []string{"Initial"}, + value: 1.23, + } + o.seconds = &mockObserver{ + lvs: []string{"Initial"}, + value: 1.23, + } + })(nextValid)(transport.NewServerContext(context.Background(), &http.Transport{}), "test:") if err != nil { - t.Errorf("expect %v, got %v", nil, err) + t.Error("The server must not throw an error.") + } + if res != "Hello valid" { + t.Error(`The server must return a "Hello valid" response.`) + } +} + +func TestClient(t *testing.T) { + e := errors.New("got an error") + nextError := func(ctx context.Context, req interface{}) (interface{}, error) { + return nil, e + } + nextValid := func(ctx context.Context, req interface{}) (interface{}, error) { + return "Hello valid", nil } - _, err = Client()(next)(context.Background(), "test:") + _, err := Client()(nextError)(context.Background(), "test:") + if err != e { + t.Error("The given error mismatch the expected.") + } + + res, err := Client(func(o *options) { + o.requests = &mockCounter{ + lvs: []string{"Initial"}, + value: 1.23, + } + o.seconds = &mockObserver{ + lvs: []string{"Initial"}, + value: 1.23, + } + })(nextValid)(transport.NewClientContext(context.Background(), &http.Transport{}), "test:") if err != nil { - t.Errorf("expect %v, got %v", nil, err) + t.Error("The server must not throw an error.") + } + if res != "Hello valid" { + t.Error(`The server must return a "Hello valid" response.`) } } diff --git a/middleware/ratelimit/ratelimit_test.go b/middleware/ratelimit/ratelimit_test.go new file mode 100644 index 000000000..2e4e5bde3 --- /dev/null +++ b/middleware/ratelimit/ratelimit_test.go @@ -0,0 +1,64 @@ +package ratelimit + +import ( + "context" + "errors" + "testing" + + "github.com/go-kratos/aegis/ratelimit" +) + +type ( + ratelimitMock struct { + reached bool + } + ratelimitReachedMock struct { + reached bool + } +) + +func (r *ratelimitMock) Allow() (ratelimit.DoneFunc, error) { + return func(_ ratelimit.DoneInfo) { + r.reached = true + }, nil +} + +func (r *ratelimitReachedMock) Allow() (ratelimit.DoneFunc, error) { + return func(_ ratelimit.DoneInfo) { + r.reached = true + }, errors.New("errored") +} + +func Test_WithLimiter(t *testing.T) { + o := options{ + limiter: &ratelimitMock{}, + } + + WithLimiter(nil)(&o) + if o.limiter != nil { + t.Error("The limiter property must be updated.") + } +} + +func Test_Server(t *testing.T) { + nextValid := func(ctx context.Context, req interface{}) (interface{}, error) { + return "Hello valid", nil + } + + rlm := &ratelimitMock{} + rlrm := &ratelimitReachedMock{} + + _, _ = Server(func(o *options) { + o.limiter = rlm + })(nextValid)(context.Background(), nil) + if !rlm.reached { + t.Error("The ratelimit must run the done function.") + } + + _, _ = Server(func(o *options) { + o.limiter = rlrm + })(nextValid)(context.Background(), nil) + if rlrm.reached { + t.Error("The ratelimit must not run the done function and should be denied.") + } +} diff --git a/middleware/recovery/recovery_test.go b/middleware/recovery/recovery_test.go index cf4a7fbb3..985cb19b0 100644 --- a/middleware/recovery/recovery_test.go +++ b/middleware/recovery/recovery_test.go @@ -6,6 +6,7 @@ import ( "testing" "github.com/go-kratos/kratos/v2/errors" + "github.com/go-kratos/kratos/v2/log" ) func TestOnce(t *testing.T) { @@ -34,3 +35,8 @@ func TestNotPanic(t *testing.T) { t.Errorf("e isn't nil") } } + +// Deprecated: Remove this test with WithLogger method. +func TestWithLogger(t *testing.T) { + _ = WithLogger(log.DefaultLogger) +} diff --git a/middleware/selector/selector_test.go b/middleware/selector/selector_test.go index afde95302..667d2755f 100644 --- a/middleware/selector/selector_test.go +++ b/middleware/selector/selector_test.go @@ -2,7 +2,6 @@ package selector import ( "context" - "fmt" "reflect" "strings" "testing" @@ -243,9 +242,20 @@ func TestHeaderFunc(t *testing.T) { func testMiddleware(handler middleware.Handler) middleware.Handler { return func(ctx context.Context, req interface{}) (reply interface{}, err error) { - fmt.Println("before") reply, err = handler(ctx, req) - fmt.Println("after") return } } + +func Test_RegexMatch(t *testing.T) { + if regexMatch("^\b(?", "something") { + t.Error("The invalid regex must not match.") + } +} + +func Test_matches(t *testing.T) { + b := Builder{} + if b.matches(context.Background(), func(_ context.Context) (transport.Transporter, bool) { return nil, false }) { + t.Error("The matches method must return false.") + } +} diff --git a/middleware/tracing/metadata_test.go b/middleware/tracing/metadata_test.go index a4f45ed69..09446c2a8 100644 --- a/middleware/tracing/metadata_test.go +++ b/middleware/tracing/metadata_test.go @@ -51,9 +51,10 @@ func TestMetadata_Extract(t *testing.T) { carrier propagation.TextMapCarrier } tests := []struct { - name string - args args - want string + name string + args args + want string + crash bool }{ { name: "https://go-kratos.dev", @@ -71,6 +72,14 @@ func TestMetadata_Extract(t *testing.T) { }, want: "https://github.com/go-kratos/kratos", }, + { + name: "https://github.com/go-kratos/kratos", + args: args{ + parent: metadata.NewServerContext(context.Background(), metadata.Metadata{}), + carrier: propagation.HeaderCarrier{"X-Md-Service-Name": nil}, + }, + crash: true, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -78,6 +87,9 @@ func TestMetadata_Extract(t *testing.T) { ctx := b.Extract(tt.args.parent, tt.args.carrier) md, ok := metadata.FromServerContext(ctx) if !ok { + if tt.crash { + return + } t.Errorf("expect %v, got %v", true, ok) } if !reflect.DeepEqual(md.Get(serviceHeader), tt.want) { diff --git a/middleware/tracing/span_test.go b/middleware/tracing/span_test.go index c930154f9..aad4614fc 100644 --- a/middleware/tracing/span_test.go +++ b/middleware/tracing/span_test.go @@ -1,11 +1,19 @@ package tracing import ( + "context" + "net" + "net/http" "reflect" "testing" + "github.com/go-kratos/kratos/v2/internal/testdata/binding" + "github.com/go-kratos/kratos/v2/metadata" + "github.com/go-kratos/kratos/v2/transport" "go.opentelemetry.io/otel/attribute" semconv "go.opentelemetry.io/otel/semconv/v1.4.0" + "go.opentelemetry.io/otel/trace" + "google.golang.org/grpc/peer" ) func Test_parseFullMethod(t *testing.T) { @@ -142,6 +150,18 @@ func Test_parseTarget(t *testing.T) { wantAddress: "hello", wantErr: false, }, + { + name: "empty", + endpoint: "%%", + wantAddress: "", + wantErr: true, + }, + { + name: "invalid path", + endpoint: "//%2F/#%2Fanother", + wantAddress: "", + wantErr: false, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -156,3 +176,74 @@ func Test_parseTarget(t *testing.T) { }) } } + +func Test_setServerSpan(t *testing.T) { + ctx := context.Background() + _, span := trace.NewNoopTracerProvider().Tracer("Tracer").Start(ctx, "Spanname") + + // Handle without Transport context + setServerSpan(ctx, span, nil) + + // Handle with proto message + m := &binding.HelloRequest{} + setServerSpan(ctx, span, m) + + // Handle with metadata context + ctx = metadata.NewServerContext(ctx, metadata.New()) + setServerSpan(ctx, span, m) + + // Handle with KindHTTP transport context + mt := &mockTransport{ + kind: transport.KindHTTP, + } + mt.request, _ = http.NewRequest(http.MethodGet, "/endpoint", nil) + ctx = transport.NewServerContext(ctx, mt) + setServerSpan(ctx, span, m) + + // Handle with KindGRPC transport context + mt.kind = transport.KindGRPC + ctx = transport.NewServerContext(ctx, mt) + ip, _ := net.ResolveIPAddr("ip", "1.1.1.1") + ctx = peer.NewContext(ctx, &peer.Peer{ + Addr: ip, + }) + setServerSpan(ctx, span, m) +} + +func Test_setClientSpan(t *testing.T) { + ctx := context.Background() + _, span := trace.NewNoopTracerProvider().Tracer("Tracer").Start(ctx, "Spanname") + + // Handle without Transport context + setClientSpan(ctx, span, nil) + + // Handle with proto message + m := &binding.HelloRequest{} + setClientSpan(ctx, span, m) + + // Handle with metadata context + ctx = metadata.NewClientContext(ctx, metadata.New()) + setClientSpan(ctx, span, m) + + // Handle with KindHTTP transport context + mt := &mockTransport{ + kind: transport.KindHTTP, + } + mt.request, _ = http.NewRequest(http.MethodGet, "/endpoint", nil) + mt.request.Host = "MyServer" + ctx = transport.NewClientContext(ctx, mt) + setClientSpan(ctx, span, m) + + // Handle with KindGRPC transport context + mt.kind = transport.KindGRPC + ctx = transport.NewClientContext(ctx, mt) + ip, _ := net.ResolveIPAddr("ip", "1.1.1.1") + ctx = peer.NewContext(ctx, &peer.Peer{ + Addr: ip, + }) + setClientSpan(ctx, span, m) + + // Handle without Host request + ctx = transport.NewClientContext(ctx, mt) + setClientSpan(ctx, span, m) +} diff --git a/middleware/tracing/statsHandler.go b/middleware/tracing/statsHandler.go index 11b12679d..b9ee3fd1e 100644 --- a/middleware/tracing/statsHandler.go +++ b/middleware/tracing/statsHandler.go @@ -2,6 +2,7 @@ package tracing import ( "context" + "fmt" "go.opentelemetry.io/otel/trace" "google.golang.org/grpc/peer" @@ -13,6 +14,7 @@ type ClientHandler struct{} // HandleConn exists to satisfy gRPC stats.Handler. func (c *ClientHandler) HandleConn(ctx context.Context, cs stats.ConnStats) { + fmt.Println("Handle connection.") } // TagConn exists to satisfy gRPC stats.Handler. diff --git a/middleware/tracing/statsHandler_test.go b/middleware/tracing/statsHandler_test.go new file mode 100644 index 000000000..d8a38dcd1 --- /dev/null +++ b/middleware/tracing/statsHandler_test.go @@ -0,0 +1,81 @@ +package tracing + +import ( + "context" + "net" + "testing" + + "go.opentelemetry.io/otel/trace" + "google.golang.org/grpc/peer" + "google.golang.org/grpc/stats" +) + +type ctxKey string + +const testKey ctxKey = "MY_TEST_KEY" + +func Test_Client_HandleConn(t *testing.T) { + (&ClientHandler{}).HandleConn(context.Background(), nil) +} + +func Test_Client_TagConn(t *testing.T) { + client := &ClientHandler{} + ctx := context.WithValue(context.Background(), testKey, 123) + + if client.TagConn(ctx, nil).Value(testKey) != 123 { + t.Errorf(`The context value must be 123 for the "MY_KEY_TEST" key, %v given.`, client.TagConn(ctx, nil).Value(testKey)) + } +} + +func Test_Client_TagRPC(t *testing.T) { + client := &ClientHandler{} + ctx := context.WithValue(context.Background(), testKey, 123) + + if client.TagRPC(ctx, nil).Value(testKey) != 123 { + t.Errorf(`The context value must be 123 for the "MY_KEY_TEST" key, %v given.`, client.TagConn(ctx, nil).Value(testKey)) + } +} + +type ( + mockSpan struct { + trace.Span + mockSpanCtx *trace.SpanContext + } +) + +func (m *mockSpan) SpanContext() trace.SpanContext { + return *m.mockSpanCtx +} + +func Test_Client_HandleRPC(t *testing.T) { + client := &ClientHandler{} + ctx := context.Background() + rs := stats.OutHeader{} + + // Handle stats.RPCStats is not type of stats.OutHeader case + client.HandleRPC(context.TODO(), nil) + + // Handle context doesn't have the peerkey filled with a Peer instance + client.HandleRPC(ctx, &rs) + + // Handle context with the peerkey filled with a Peer instance + ip, _ := net.ResolveIPAddr("ip", "1.1.1.1") + ctx = peer.NewContext(ctx, &peer.Peer{ + Addr: ip, + }) + client.HandleRPC(ctx, &rs) + + // Handle context with Span + _, span := trace.NewNoopTracerProvider().Tracer("Tracer").Start(ctx, "Spanname") + spanCtx := trace.SpanContext{} + spanID := [8]byte{12, 12, 12, 12, 12, 12, 12, 12} + traceID := [16]byte{12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12} + spanCtx = spanCtx.WithTraceID(traceID) + spanCtx = spanCtx.WithSpanID(spanID) + mSpan := mockSpan{ + Span: span, + mockSpanCtx: &spanCtx, + } + ctx = trace.ContextWithSpan(ctx, &mSpan) + client.HandleRPC(ctx, &rs) +} diff --git a/middleware/tracing/tracer_test.go b/middleware/tracing/tracer_test.go new file mode 100644 index 000000000..fbd51c070 --- /dev/null +++ b/middleware/tracing/tracer_test.go @@ -0,0 +1,54 @@ +package tracing + +import ( + "context" + "errors" + "testing" + + "github.com/go-kratos/kratos/v2/internal/testdata/binding" + "go.opentelemetry.io/otel/trace" +) + +func Test_NewTracer(t *testing.T) { + tracer := NewTracer(trace.SpanKindClient, func(o *options) { + o.tracerProvider = trace.NewNoopTracerProvider() + }) + + if tracer.kind != trace.SpanKindClient { + t.Errorf("The tracer kind must be equal to trace.SpanKindClient, %v given.", tracer.kind) + } + + defer func() { + if recover() == nil { + t.Error("The NewTracer with an invalid SpanKindMustCrash must panic") + } + }() + _ = NewTracer(666, func(o *options) { + o.tracerProvider = trace.NewNoopTracerProvider() + }) +} + +func Test_Tracer_End(t *testing.T) { + tracer := NewTracer(trace.SpanKindClient, func(o *options) { + o.tracerProvider = trace.NewNoopTracerProvider() + }) + ctx, span := trace.NewNoopTracerProvider().Tracer("noop").Start(context.Background(), "noopSpan") + + // Handle with error case + tracer.End(ctx, span, nil, errors.New("dummy error")) + + // Handle without error case + tracer.End(ctx, span, nil, nil) + + m := &binding.HelloRequest{} + + // Handle the trace KindServer + tracer = NewTracer(trace.SpanKindServer, func(o *options) { + o.tracerProvider = trace.NewNoopTracerProvider() + }) + tracer.End(ctx, span, m, nil) + tracer = NewTracer(trace.SpanKindClient, func(o *options) { + o.tracerProvider = trace.NewNoopTracerProvider() + }) + tracer.End(ctx, span, m, nil) +} diff --git a/middleware/tracing/tracing_test.go b/middleware/tracing/tracing_test.go index d5fc0ed66..8de2ceea4 100644 --- a/middleware/tracing/tracing_test.go +++ b/middleware/tracing/tracing_test.go @@ -42,6 +42,7 @@ type mockTransport struct { endpoint string operation string header headerCarrier + request *http.Request } func (tr *mockTransport) Kind() transport.Kind { return tr.kind } @@ -49,6 +50,16 @@ func (tr *mockTransport) Endpoint() string { return tr.endpoint } func (tr *mockTransport) Operation() string { return tr.operation } func (tr *mockTransport) RequestHeader() transport.Header { return tr.header } func (tr *mockTransport) ReplyHeader() transport.Header { return tr.header } +func (tr *mockTransport) Request() *http.Request { + if tr.request == nil { + rq, _ := http.NewRequest(http.MethodGet, "/endpoint", nil) + + return rq + } + + return tr.request +} +func (tr *mockTransport) PathTemplate() string { return "" } func TestTracer(t *testing.T) { carrier := headerCarrier{} diff --git a/options_test.go b/options_test.go index 4880bd195..438de3d83 100644 --- a/options_test.go +++ b/options_test.go @@ -143,3 +143,12 @@ func TestRegistrarTimeout(t *testing.T) { t.Fatal("o.registrarTimeout is not equal to v") } } + +func TestStopTimeout(t *testing.T) { + o := &options{} + v := time.Duration(123) + StopTimeout(v)(o) + if !reflect.DeepEqual(v, o.stopTimeout) { + t.Fatal("o.stopTimeout is not equal to v") + } +} diff --git a/transport/grpc/codec.go b/transport/grpc/codec.go index 1b2c82893..19670bbab 100644 --- a/transport/grpc/codec.go +++ b/transport/grpc/codec.go @@ -3,6 +3,7 @@ package grpc import ( "fmt" + enc "github.com/go-kratos/kratos/v2/encoding" "github.com/go-kratos/kratos/v2/encoding/json" "google.golang.org/grpc/encoding" "google.golang.org/protobuf/proto" @@ -20,7 +21,7 @@ func (codec) Marshal(v interface{}) ([]byte, error) { if !ok { return nil, fmt.Errorf("failed to marshal, message is %T, want proto.Message", v) } - return json.MarshalOptions.Marshal(vv) + return enc.GetCodec(json.Name).Marshal(vv) } func (codec) Unmarshal(data []byte, v interface{}) error { @@ -28,9 +29,9 @@ func (codec) Unmarshal(data []byte, v interface{}) error { if !ok { return fmt.Errorf("failed to unmarshal, message is %T, want proto.Message", v) } - return json.UnmarshalOptions.Unmarshal(data, vv) + return enc.GetCodec(json.Name).Unmarshal(data, vv) } func (codec) Name() string { - return "json" + return json.Name } diff --git a/transport/grpc/interceptor.go b/transport/grpc/interceptor.go index 3f3827f69..ccd83ea69 100644 --- a/transport/grpc/interceptor.go +++ b/transport/grpc/interceptor.go @@ -17,12 +17,15 @@ func (s *Server) unaryServerInterceptor() grpc.UnaryServerInterceptor { defer cancel() md, _ := grpcmd.FromIncomingContext(ctx) replyHeader := grpcmd.MD{} - ctx = transport.NewServerContext(ctx, &Transport{ - endpoint: s.endpoint.String(), + tr := &Transport{ operation: info.FullMethod, reqHeader: headerCarrier(md), replyHeader: headerCarrier(replyHeader), - }) + } + if s.endpoint != nil { + tr.endpoint = s.endpoint.String() + } + ctx = transport.NewServerContext(ctx, tr) if s.timeout > 0 { ctx, cancel = context.WithTimeout(ctx, s.timeout) defer cancel() @@ -30,8 +33,8 @@ func (s *Server) unaryServerInterceptor() grpc.UnaryServerInterceptor { h := func(ctx context.Context, req interface{}) (interface{}, error) { return handler(ctx, req) } - if len(s.middleware) > 0 { - h = middleware.Chain(s.middleware...)(h) + if next := s.middleware.Match(tr.Operation()); len(next) > 0 { + h = middleware.Chain(next...)(h) } reply, err := h(ctx, req) if len(replyHeader) > 0 { diff --git a/transport/grpc/resolver/direct/builder_test.go b/transport/grpc/resolver/direct/builder_test.go index a0d1d31ab..1cd6bf402 100644 --- a/transport/grpc/resolver/direct/builder_test.go +++ b/transport/grpc/resolver/direct/builder_test.go @@ -1,6 +1,7 @@ package direct import ( + "fmt" "reflect" "testing" @@ -15,9 +16,14 @@ func TestDirectBuilder_Scheme(t *testing.T) { } } -type mockConn struct{} +type mockConn struct { + needUpdateStateErr bool +} func (m *mockConn) UpdateState(resolver.State) error { + if m.needUpdateStateErr { + return fmt.Errorf("mock test needUpdateStateErr") + } return nil } @@ -38,4 +44,11 @@ func TestDirectBuilder_Build(t *testing.T) { t.Errorf("expect no error, got %v", err) } r.ResolveNow(resolver.ResolveNowOptions{}) + r.Close() + + // need update state err + _, err = b.Build(resolver.Target{}, &mockConn{needUpdateStateErr: true}, resolver.BuildOptions{}) + if err == nil { + t.Errorf("expect needUpdateStateErr, got nil") + } } diff --git a/transport/grpc/resolver/discovery/builder.go b/transport/grpc/resolver/discovery/builder.go index 7ce777176..a4425a518 100644 --- a/transport/grpc/resolver/discovery/builder.go +++ b/transport/grpc/resolver/discovery/builder.go @@ -59,18 +59,24 @@ func NewBuilder(d registry.Discovery, opts ...Option) resolver.Builder { } func (b *builder) Build(target resolver.Target, cc resolver.ClientConn, opts resolver.BuildOptions) (resolver.Resolver, error) { - var ( + watchRes := &struct { err error w registry.Watcher - ) + }{} + done := make(chan struct{}, 1) ctx, cancel := context.WithCancel(context.Background()) go func() { - w, err = b.discoverer.Watch(ctx, strings.TrimPrefix(target.URL.Path, "/")) + w, err := b.discoverer.Watch(ctx, strings.TrimPrefix(target.URL.Path, "/")) + watchRes.w = w + watchRes.err = err close(done) }() + + var err error select { case <-done: + err = watchRes.err case <-time.After(b.timeout): err = errors.New("discovery create watcher overtime") } @@ -79,7 +85,7 @@ func (b *builder) Build(target resolver.Target, cc resolver.ClientConn, opts res return nil, err } r := &discoveryResolver{ - w: w, + w: watchRes.w, cc: cc, ctx: ctx, cancel: cancel, diff --git a/transport/grpc/resolver/discovery/builder_test.go b/transport/grpc/resolver/discovery/builder_test.go index 7a06abfcd..11541cb76 100644 --- a/transport/grpc/resolver/discovery/builder_test.go +++ b/transport/grpc/resolver/discovery/builder_test.go @@ -28,6 +28,14 @@ func TestWithTimeout(t *testing.T) { } } +func TestDisableDebugLog(t *testing.T) { + o := &builder{} + DisableDebugLog()(o) + if !o.debugLogDisabled { + t.Errorf("expected debugLogDisabled true, got %v", o.debugLogDisabled) + } +} + type mockDiscovery struct{} func (m *mockDiscovery) GetService(ctx context.Context, serviceName string) ([]*registry.ServiceInstance, error) { @@ -35,6 +43,7 @@ func (m *mockDiscovery) GetService(ctx context.Context, serviceName string) ([]* } func (m *mockDiscovery) Watch(ctx context.Context, serviceName string) (registry.Watcher, error) { + time.Sleep(time.Microsecond * 500) return &testWatch{}, nil } @@ -62,9 +71,29 @@ func (m *mockConn) ParseServiceConfig(serviceConfigJSON string) *serviceconfig.P } func TestBuilder_Build(t *testing.T) { - b := NewBuilder(&mockDiscovery{}) - _, err := b.Build(resolver.Target{Scheme: resolver.GetDefaultScheme(), Endpoint: "gprc://authority/endpoint"}, &mockConn{}, resolver.BuildOptions{}) + b := NewBuilder(&mockDiscovery{}, DisableDebugLog()) + _, err := b.Build( + resolver.Target{ + Scheme: resolver.GetDefaultScheme(), + Endpoint: "gprc://authority/endpoint", + }, + &mockConn{}, + resolver.BuildOptions{}, + ) if err != nil { t.Errorf("expected no error, got %v", err) + return + } + timeoutBuilder := NewBuilder(&mockDiscovery{}, WithTimeout(0)) + _, err = timeoutBuilder.Build( + resolver.Target{ + Scheme: resolver.GetDefaultScheme(), + Endpoint: "gprc://authority/endpoint", + }, + &mockConn{}, + resolver.BuildOptions{}, + ) + if err == nil { + t.Errorf("expected error, got %v", err) } } diff --git a/transport/grpc/resolver/discovery/resolver.go b/transport/grpc/resolver/discovery/resolver.go index 6a7f6e7e8..0dc115b65 100644 --- a/transport/grpc/resolver/discovery/resolver.go +++ b/transport/grpc/resolver/discovery/resolver.go @@ -49,23 +49,23 @@ func (r *discoveryResolver) update(ins []*registry.ServiceInstance) { addrs := make([]resolver.Address, 0) endpoints := make(map[string]struct{}) for _, in := range ins { - endpoint, err := endpoint.ParseEndpoint(in.Endpoints, endpoint.Scheme("grpc", !r.insecure)) + ept, err := endpoint.ParseEndpoint(in.Endpoints, endpoint.Scheme("grpc", !r.insecure)) if err != nil { log.Errorf("[resolver] Failed to parse discovery endpoint: %v", err) continue } - if endpoint == "" { + if ept == "" { continue } // filter redundant endpoints - if _, ok := endpoints[endpoint]; ok { + if _, ok := endpoints[ept]; ok { continue } - endpoints[endpoint] = struct{}{} + endpoints[ept] = struct{}{} addr := resolver.Address{ ServerName: in.Name, Attributes: parseAttributes(in.Metadata), - Addr: endpoint, + Addr: ept, } addr.Attributes = addr.Attributes.WithValue("rawServiceInstance", in) addrs = append(addrs, addr) diff --git a/transport/grpc/resolver/discovery/resolver_test.go b/transport/grpc/resolver/discovery/resolver_test.go index 1d196382b..33d9fc27e 100644 --- a/transport/grpc/resolver/discovery/resolver_test.go +++ b/transport/grpc/resolver/discovery/resolver_test.go @@ -23,10 +23,16 @@ func (t *testClientConn) UpdateState(s resolver.State) error { type testWatch struct { err error + + count uint } func (m *testWatch) Next() ([]*registry.ServiceInstance, error) { time.Sleep(time.Millisecond * 200) + if m.count > 1 { + return nil, nil + } + m.count++ ins := []*registry.ServiceInstance{ { ID: "mock_ID", @@ -59,6 +65,7 @@ func TestWatch(t *testing.T) { cancel: cancel, insecure: false, } + r.ResolveNow(resolver.ResolveNowOptions{}) go func() { time.Sleep(time.Second * 2) r.Close() @@ -102,7 +109,10 @@ func TestWatchContextCancel(t *testing.T) { } func TestParseAttributes(t *testing.T) { - a := parseAttributes(map[string]string{"a": "b"}) + a := parseAttributes(map[string]string{ + "a": "b", + "c": "d", + }) if !reflect.DeepEqual("b", a.Value("a").(string)) { t.Errorf("expect b, got %v", a.Value("a")) } diff --git a/transport/grpc/server.go b/transport/grpc/server.go index 31c03f75e..b66ba932d 100644 --- a/transport/grpc/server.go +++ b/transport/grpc/server.go @@ -8,6 +8,7 @@ import ( "time" "github.com/go-kratos/kratos/v2/internal/endpoint" + "github.com/go-kratos/kratos/v2/internal/matcher" apimd "github.com/go-kratos/kratos/v2/api/metadata" @@ -62,7 +63,7 @@ func Logger(logger log.Logger) ServerOption { // Middleware with server middleware. func Middleware(m ...middleware.Middleware) ServerOption { return func(s *Server) { - s.middleware = m + s.middleware.Use(m...) } } @@ -112,7 +113,7 @@ type Server struct { address string endpoint *url.URL timeout time.Duration - middleware []middleware.Middleware + middleware matcher.Matcher unaryInts []grpc.UnaryServerInterceptor streamInts []grpc.StreamServerInterceptor grpcOpts []grpc.ServerOption @@ -123,11 +124,12 @@ type Server struct { // NewServer creates a gRPC server by options. func NewServer(opts ...ServerOption) *Server { srv := &Server{ - baseCtx: context.Background(), - network: "tcp", - address: ":0", - timeout: 1 * time.Second, - health: health.NewServer(), + baseCtx: context.Background(), + network: "tcp", + address: ":0", + timeout: 1 * time.Second, + health: health.NewServer(), + middleware: matcher.New(), } for _, o := range opts { o(srv) @@ -156,8 +158,6 @@ func NewServer(opts ...ServerOption) *Server { } srv.Server = grpc.NewServer(grpcOpts...) srv.metadata = apimd.NewServer(srv.Server) - // listen and endpoint - srv.err = srv.listenAndEndpoint() // internal register grpc_health_v1.RegisterHealthServer(srv.Server, srv.health) apimd.RegisterMetadataServer(srv.Server, srv.metadata) @@ -165,11 +165,20 @@ func NewServer(opts ...ServerOption) *Server { return srv } +// Use uses a service middleware with selector. +// selector: +// - '/*' +// - '/helloworld.v1.Greeter/*' +// - '/helloworld.v1.Greeter/SayHello' +func (s *Server) Use(selector string, m ...middleware.Middleware) { + s.middleware.Add(selector, m...) +} + // Endpoint return a real address to registry endpoint. // examples: // grpc://127.0.0.1:9000?isSecure=false func (s *Server) Endpoint() (*url.URL, error) { - if s.err != nil { + if err := s.listenAndEndpoint(); err != nil { return nil, s.err } return s.endpoint, nil @@ -177,7 +186,7 @@ func (s *Server) Endpoint() (*url.URL, error) { // Start start the gRPC server. func (s *Server) Start(ctx context.Context) error { - if s.err != nil { + if err := s.listenAndEndpoint(); err != nil { return s.err } s.baseCtx = ctx @@ -198,15 +207,18 @@ func (s *Server) listenAndEndpoint() error { if s.lis == nil { lis, err := net.Listen(s.network, s.address) if err != nil { + s.err = err return err } s.lis = lis } - addr, err := host.Extract(s.address, s.lis) - if err != nil { - _ = s.lis.Close() - return err + if s.endpoint == nil { + addr, err := host.Extract(s.address, s.lis) + if err != nil { + s.err = err + return err + } + s.endpoint = endpoint.NewEndpoint(endpoint.Scheme("grpc", s.tlsConf != nil), addr) } - s.endpoint = endpoint.NewEndpoint(endpoint.Scheme("grpc", s.tlsConf != nil), addr) - return nil + return s.err } diff --git a/transport/grpc/server_test.go b/transport/grpc/server_test.go index 5dd779f37..fd1ae6757 100644 --- a/transport/grpc/server_test.go +++ b/transport/grpc/server_test.go @@ -12,10 +12,10 @@ import ( "time" "github.com/go-kratos/kratos/v2/errors" + "github.com/go-kratos/kratos/v2/internal/matcher" pb "github.com/go-kratos/kratos/v2/internal/testdata/helloworld" "github.com/go-kratos/kratos/v2/middleware" "github.com/go-kratos/kratos/v2/transport" - "google.golang.org/grpc" ) @@ -24,6 +24,36 @@ type server struct { pb.UnimplementedGreeterServer } +func (s *server) SayHelloStream(streamServer pb.Greeter_SayHelloStreamServer) error { + tctx, ok := transport.FromServerContext(streamServer.Context()) + if ok { + tctx.ReplyHeader().Set("123", "123") + } + var cnt uint + for { + in, err := streamServer.Recv() + if err != nil { + return err + } + if in.Name == "error" { + return errors.BadRequest("custom_error", fmt.Sprintf("invalid argument %s", in.Name)) + } + if in.Name == "panic" { + panic("server panic") + } + err = streamServer.Send(&pb.HelloReply{ + Message: fmt.Sprintf("hello %s", in.Name), + }) + if err != nil { + return err + } + cnt++ + if cnt > 1 { + return nil + } + } +} + // SayHello implements helloworld.GreeterServer func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) { if in.Name == "error" { @@ -97,6 +127,9 @@ func testClient(t *testing.T, srv *Server) { } }), ) + defer func() { + _ = conn.Close() + }() if err != nil { t.Fatal(err) } @@ -109,7 +142,28 @@ func testClient(t *testing.T, srv *Server) { if !reflect.DeepEqual(reply.Message, "Hello kratos") { t.Errorf("expect %s, got %s", "Hello kratos", reply.Message) } - _ = conn.Close() + + streamCli, err := client.SayHelloStream(context.Background()) + if err != nil { + t.Error(err) + return + } + defer func() { + _ = streamCli.CloseSend() + }() + err = streamCli.Send(&pb.HelloRequest{Name: "cc"}) + if err != nil { + t.Error(err) + return + } + reply, err = streamCli.Recv() + if err != nil { + t.Error(err) + return + } + if !reflect.DeepEqual(reply.Message, "hello cc") { + t.Errorf("expect %s, got %s", "hello cc", reply.Message) + } } func TestNetwork(t *testing.T) { @@ -145,17 +199,6 @@ func TestTimeout(t *testing.T) { } } -func TestMiddleware(t *testing.T) { - o := &Server{} - v := []middleware.Middleware{ - func(middleware.Handler) middleware.Handler { return nil }, - } - Middleware(v...)(o) - if !reflect.DeepEqual(v, o.middleware) { - t.Errorf("expect %v, got %v", v, o.middleware) - } -} - func TestTLSConfig(t *testing.T) { o := &Server{} v := &tls.Config{} @@ -220,9 +263,10 @@ func TestServer_unaryServerInterceptor(t *testing.T) { srv := &Server{ baseCtx: context.Background(), endpoint: u, - middleware: []middleware.Middleware{EmptyMiddleware()}, timeout: time.Duration(10), + middleware: matcher.New(), } + srv.middleware.Use(EmptyMiddleware()) req := &struct{}{} rv, err := srv.unaryServerInterceptor()(context.TODO(), req, &grpc.UnaryServerInfo{}, func(ctx context.Context, req interface{}) (i interface{}, e error) { return &testResp{Data: "hi"}, nil @@ -236,10 +280,16 @@ func TestServer_unaryServerInterceptor(t *testing.T) { } func TestListener(t *testing.T) { - lis := &net.TCPListener{} + lis, err := net.Listen("tcp", ":0") + if err != nil { + t.Fatal(err) + } s := &Server{} Listener(lis)(s) if !reflect.DeepEqual(lis, s.lis) { t.Errorf("expect %v, got %v", lis, s.lis) } + if e, err := s.Endpoint(); err != nil || e == nil { + t.Errorf("expect not empty") + } } diff --git a/transport/http/binding/encode.go b/transport/http/binding/encode.go index 14faf2e92..3129bbe5d 100644 --- a/transport/http/binding/encode.go +++ b/transport/http/binding/encode.go @@ -12,20 +12,19 @@ import ( protoreflect "google.golang.org/protobuf/reflect/protoreflect" ) +var reg = regexp.MustCompile(`/{[\\.\w]+}`) + // EncodeURL encode proto message to url path. func EncodeURL(pathTemplate string, msg proto.Message, needQuery bool) string { if msg == nil || (reflect.ValueOf(msg).Kind() == reflect.Ptr && reflect.ValueOf(msg).IsNil()) { return pathTemplate } - reg := regexp.MustCompile(`/{[.\w]+}`) - if reg == nil { - return pathTemplate - } pathParams := make(map[string]struct{}) path := reg.ReplaceAllStringFunc(pathTemplate, func(in string) string { - if len(in) < 4 { //nolint:gomnd // ** explain the 4 number here :-) ** - return in - } + // it's unreachable because the reg means that must have more than one char in {} + //if len(in) < 4 { //nolint:gomnd // ** explain the 4 number here :-) ** + // return in + //} key := in[2 : len(in)-1] vars := strings.Split(key, ".") value, err := getValueByField(msg.ProtoReflect(), vars) diff --git a/transport/http/binding/encode_test.go b/transport/http/binding/encode_test.go index 5e0618f0d..fdc2bb78a 100644 --- a/transport/http/binding/encode_test.go +++ b/transport/http/binding/encode_test.go @@ -14,6 +14,34 @@ func TestProtoPath(t *testing.T) { if url != `http://helloworld.Greeter/helloworld/test/sub/2233!!!` { t.Fatalf("proto path not expected!actual: %s ", url) } + url = EncodeURL("http://helloworld.Greeter/helloworld/{name}/sub/{sub.name}", nil, false) + fmt.Println(url) + if url != "http://helloworld.Greeter/helloworld/{name}/sub/{sub.name}" { + t.Fatalf("proto path not expected!actual: %s ", url) + } + url = EncodeURL("http://helloworld.Greeter/helloworld/{}/sub/{sub.name}", &binding.HelloRequest{Name: "test", Sub: &binding.Sub{Name: "hello"}}, false) + fmt.Println(url) + if url != "http://helloworld.Greeter/helloworld/{}/sub/hello" { + t.Fatalf("proto path not expected!actual: %s ", url) + } + url = EncodeURL("http://helloworld.Greeter/helloworld/{}/sub/{sub.name.cc}", &binding.HelloRequest{Name: "test", Sub: &binding.Sub{Name: "hello"}}, false) + fmt.Println(url) + if url != "http://helloworld.Greeter/helloworld/{}/sub/{sub.name.cc}" { + t.Fatalf("proto path not expected!actual: %s ", url) + } + + url = EncodeURL( + "http://helloworld.Greeter/helloworld/{}/sub/{test_repeated.1}", + &binding.HelloRequest{ + Name: "test", Sub: &binding.Sub{Name: "hello"}, + TestRepeated: []string{"123", "456"}, + }, + false, + ) + fmt.Println(url) + if url != "http://helloworld.Greeter/helloworld/{}/sub/{test_repeated.1}" { + t.Fatalf("proto path not expected!actual: %s ", url) + } url = EncodeURL("http://helloworld.Greeter/helloworld/{name}/sub/{sub.naming}", &binding.HelloRequest{Name: "test", Sub: &binding.Sub{Name: "5566!!!"}}, false) fmt.Println(url) @@ -44,7 +72,7 @@ func TestProtoPath(t *testing.T) { Sub: &binding.Sub{Name: "kratos"}, }, true) fmt.Println(url) - if url != `http://helloworld.Greeter/helloworld/go/sub?sub.naming=kratos&updateMask=` { + if url != `http://helloworld.Greeter/helloworld/go/sub?sub.naming=kratos` { t.Fatalf("proto path not expected!actual: %s ", url) } diff --git a/transport/http/client_test.go b/transport/http/client_test.go index 731d111dc..c768b364b 100644 --- a/transport/http/client_test.go +++ b/transport/http/client_test.go @@ -8,6 +8,7 @@ import ( "errors" "fmt" "io" + "log" nethttp "net/http" "reflect" "strconv" @@ -17,6 +18,7 @@ import ( kratosErrors "github.com/go-kratos/kratos/v2/errors" "github.com/go-kratos/kratos/v2/middleware" "github.com/go-kratos/kratos/v2/registry" + "github.com/go-kratos/kratos/v2/selector" ) type mockRoundTripper struct{} @@ -25,6 +27,21 @@ func (rt *mockRoundTripper) RoundTrip(req *nethttp.Request) (resp *nethttp.Respo return } +type mockCallOption struct { + needErr bool +} + +func (x *mockCallOption) before(info *callInfo) error { + if x.needErr { + return fmt.Errorf("option need return err") + } + return nil +} + +func (x *mockCallOption) after(info *callInfo, attempt *csAttempt) { + log.Println("run in mockCallOption.after") +} + func TestWithTransport(t *testing.T) { ov := &mockRoundTripper{} o := WithTransport(ov) @@ -165,6 +182,16 @@ func TestWithDiscovery(t *testing.T) { } } +func TestWithSelector(t *testing.T) { + ov := &selector.Default{} + o := WithSelector(ov) + co := &clientOptions{} + o(co) + if !reflect.DeepEqual(co.selector, ov) { + t.Errorf("expected selector to be %v, got %v", ov, co.selector) + } +} + func TestDefaultRequestEncoder(t *testing.T) { req1 := &nethttp.Request{ Header: make(nethttp.Header), @@ -284,10 +311,6 @@ func TestNewClient(t *testing.T) { if err != nil { t.Error(err) } - client, err := NewClient(context.Background(), WithDiscovery(&mockDiscovery{}), WithEndpoint("discovery:///go-kratos")) - if err != nil { - t.Error(err) - } _, err = NewClient(context.Background(), WithDiscovery(&mockDiscovery{}), WithEndpoint("discovery:///go-kratos")) if err != nil { t.Error(err) @@ -296,13 +319,43 @@ func TestNewClient(t *testing.T) { if err != nil { t.Error(err) } + _, err = NewClient(context.Background(), WithEndpoint("127.0.0.1:8888:xxxxa")) + if err == nil { + t.Error("except a parseTarget error") + } _, err = NewClient(context.Background(), WithDiscovery(&mockDiscovery{}), WithEndpoint("https://go-kratos.dev/")) if err == nil { t.Error("err should not be equal to nil") } - err = client.Invoke(context.Background(), "POST", "/go", map[string]string{"name": "kratos"}, nil, EmptyCallOption{}) + client, err := NewClient( + context.Background(), + WithDiscovery(&mockDiscovery{}), + WithEndpoint("discovery:///go-kratos"), + WithMiddleware(func(handler middleware.Handler) middleware.Handler { + t.Logf("handle in middleware") + return func(ctx context.Context, req interface{}) (interface{}, error) { + return handler(ctx, req) + } + }), + ) + if err != nil { + t.Error(err) + } + + err = client.Invoke(context.Background(), "POST", "/go", map[string]string{"name": "kratos"}, nil, EmptyCallOption{}, &mockCallOption{}) if err == nil { t.Error("err should not be equal to nil") } + err = client.Invoke(context.Background(), "POST", "/go", map[string]string{"name": "kratos"}, nil, EmptyCallOption{}, &mockCallOption{needErr: true}) + if err == nil { + t.Error("err should be equal to callOption err") + } + client.opts.encoder = func(ctx context.Context, contentType string, in interface{}) (body []byte, err error) { + return nil, fmt.Errorf("mock test encoder error") + } + err = client.Invoke(context.Background(), "POST", "/go", map[string]string{"name": "kratos"}, nil, EmptyCallOption{}) + if err == nil { + t.Error("err should be equal to encoder error") + } } diff --git a/transport/http/context.go b/transport/http/context.go index bc54134f5..347d93ffe 100644 --- a/transport/http/context.go +++ b/transport/http/context.go @@ -10,6 +10,7 @@ import ( "time" "github.com/go-kratos/kratos/v2/middleware" + "github.com/go-kratos/kratos/v2/transport" "github.com/go-kratos/kratos/v2/transport/http/binding" "github.com/gorilla/mux" ) @@ -89,7 +90,10 @@ func (c *wrapper) Query() url.Values { func (c *wrapper) Request() *http.Request { return c.req } func (c *wrapper) Response() http.ResponseWriter { return c.res } func (c *wrapper) Middleware(h middleware.Handler) middleware.Handler { - return middleware.Chain(c.router.srv.ms...)(h) + if tr, ok := transport.FromServerContext(c.req.Context()); ok { + return middleware.Chain(c.router.srv.middleware.Match(tr.Operation())...)(h) + } + return middleware.Chain(c.router.srv.middleware.Match(c.req.URL.Path)...)(h) } func (c *wrapper) Bind(v interface{}) error { return c.router.srv.dec(c.req, v) } func (c *wrapper) BindVars(v interface{}) error { return binding.BindQuery(c.Vars(), v) } diff --git a/transport/http/context_test.go b/transport/http/context_test.go index 85a7aaaa0..3b7466bf0 100644 --- a/transport/http/context_test.go +++ b/transport/http/context_test.go @@ -3,6 +3,8 @@ package http import ( "bytes" "context" + "errors" + "fmt" "net/http" "net/http/httptest" "net/url" @@ -90,6 +92,11 @@ func TestContextResponse(t *testing.T) { if err != nil { t.Errorf("expected %v, got %v", nil, err) } + needErr := fmt.Errorf("some error") + err = w.Returns(map[string]string{}, needErr) + if !errors.Is(err, needErr) { + t.Errorf("expected %v, got %v", needErr, err) + } } func TestContextBindQuery(t *testing.T) { diff --git a/transport/http/resolver_test.go b/transport/http/resolver_test.go index 419bae44a..27473d12b 100644 --- a/transport/http/resolver_test.go +++ b/transport/http/resolver_test.go @@ -61,22 +61,42 @@ func (m *mockRebalancer) Apply(nodes []selector.Node) {} type mockDiscoveries struct { isSecure bool + nextErr bool + stopErr bool } func (d *mockDiscoveries) GetService(ctx context.Context, serviceName string) ([]*registry.ServiceInstance, error) { return nil, nil } +const errServiceName = "needErr" + func (d *mockDiscoveries) Watch(ctx context.Context, serviceName string) (registry.Watcher, error) { - return &mockWatch{isSecure: d.isSecure}, nil + if serviceName == errServiceName { + return nil, fmt.Errorf("mock test service name watch err") + } + return &mockWatch{ctx: ctx, isSecure: d.isSecure, nextErr: d.nextErr, stopErr: d.stopErr}, nil } type mockWatch struct { + ctx context.Context + isSecure bool count int + + nextErr bool + stopErr bool } func (m *mockWatch) Next() ([]*registry.ServiceInstance, error) { + select { + case <-m.ctx.Done(): + return nil, m.ctx.Err() + default: + } + if m.nextErr { + return nil, errors.New("mock test error") + } if m.count == 1 { return nil, errors.New("mock test error") } @@ -95,21 +115,61 @@ func (m *mockWatch) Next() ([]*registry.ServiceInstance, error) { } func (m *mockWatch) Stop() error { + if m.stopErr { + return fmt.Errorf("mock test error") + } + // 标记 next 需要报错 + m.nextErr = true return nil } func TestResolver(t *testing.T) { - ta := &Target{ - Scheme: "http", - Authority: "", - Endpoint: "discovery://helloworld", + ta, err := parseTarget("discovery://helloworld", true) + if err != nil { + t.Errorf("parse err %v", err) + return + } + + // 异步 无需报错 + _, err = newResolver(context.Background(), &mockDiscoveries{true, false, false}, ta, &mockRebalancer{}, false, false) + if err != nil { + t.Errorf("expect %v, got %v", nil, err) } - _, err := newResolver(context.Background(), &mockDiscoveries{true}, ta, &mockRebalancer{}, false, false) + + // 同步 一切正常运行 + _, err = newResolver(context.Background(), &mockDiscoveries{false, false, false}, ta, &mockRebalancer{}, true, true) if err != nil { t.Errorf("expect %v, got %v", nil, err) } - _, err = newResolver(context.Background(), &mockDiscoveries{false}, ta, &mockRebalancer{}, true, true) + + // 同步 但是 next 出错 以及 stop 出错 + _, err = newResolver(context.Background(), &mockDiscoveries{false, true, true}, ta, &mockRebalancer{}, true, true) + if err == nil { + t.Errorf("expect err, got nil") + } + + // 同步 service name watch 失败 + _, err = newResolver(context.Background(), &mockDiscoveries{false, true, true}, &Target{ + Scheme: "discovery", + Endpoint: errServiceName, + }, &mockRebalancer{}, true, true) + if err == nil { + t.Errorf("expect err, got nil") + } + + cancelCtx, cancel := context.WithCancel(context.Background()) + cancel() + + // 此处应该打印出来 context.Canceled + r, err := newResolver(cancelCtx, &mockDiscoveries{false, false, false}, ta, &mockRebalancer{}, false, false) if err != nil { t.Errorf("expect %v, got %v", nil, err) } + _ = r.Close() + + // 同步 但是服务取消,此时需要报错 + _, err = newResolver(cancelCtx, &mockDiscoveries{false, false, true}, ta, &mockRebalancer{}, true, true) + if err == nil { + t.Errorf("expect ctx cancel err, got nil") + } } diff --git a/transport/http/router.go b/transport/http/router.go index d8d5c08e8..1575ff112 100644 --- a/transport/http/router.go +++ b/transport/http/router.go @@ -6,6 +6,15 @@ import ( "sync" ) +// WalkRouteFunc is the type of the function called for each route visited by Walk. +type WalkRouteFunc func(RouteInfo) error + +// RouteInfo is an HTTP route info. +type RouteInfo struct { + Path string + Method string +} + // HandlerFunc defines a function to serve HTTP requests. type HandlerFunc func(Context) error diff --git a/transport/http/server.go b/transport/http/server.go index c45a72697..4767e79b4 100644 --- a/transport/http/server.go +++ b/transport/http/server.go @@ -10,6 +10,7 @@ import ( "time" "github.com/go-kratos/kratos/v2/internal/endpoint" + "github.com/go-kratos/kratos/v2/internal/matcher" "github.com/go-kratos/kratos/v2/internal/host" "github.com/go-kratos/kratos/v2/log" @@ -22,6 +23,7 @@ import ( var ( _ transport.Server = (*Server)(nil) _ transport.Endpointer = (*Server)(nil) + _ http.Handler = (*Server)(nil) ) // ServerOption is an HTTP server option. @@ -57,7 +59,7 @@ func Logger(logger log.Logger) ServerOption { // Middleware with service middleware option. func Middleware(m ...middleware.Middleware) ServerOption { return func(o *Server) { - o.ms = m + o.middleware.Use(m...) } } @@ -123,7 +125,7 @@ type Server struct { address string timeout time.Duration filters []FilterFunc - ms []middleware.Middleware + middleware matcher.Matcher dec DecodeRequestFunc enc EncodeResponseFunc ene EncodeErrorFunc @@ -137,6 +139,7 @@ func NewServer(opts ...ServerOption) *Server { network: "tcp", address: ":0", timeout: 1 * time.Second, + middleware: matcher.New(), dec: DefaultRequestDecoder, enc: DefaultResponseEncoder, ene: DefaultErrorEncoder, @@ -153,10 +156,38 @@ func NewServer(opts ...ServerOption) *Server { Handler: FilterChain(srv.filters...)(srv.router), TLSConfig: srv.tlsConf, } - srv.err = srv.listenAndEndpoint() return srv } +// Use uses a service middleware with selector. +// selector: +// - '/*' +// - '/helloworld.v1.Greeter/*' +// - '/helloworld.v1.Greeter/SayHello' +func (s *Server) Use(selector string, m ...middleware.Middleware) { + s.middleware.Add(selector, m...) +} + +// WalkRoute walks the router and all its sub-routers, calling walkFn for each route in the tree. +func (s *Server) WalkRoute(fn WalkRouteFunc) error { + return s.router.Walk(func(route *mux.Route, router *mux.Router, ancestors []*mux.Route) error { + methods, err := route.GetMethods() + if err != nil { + return nil // ignore no methods + } + path, err := route.GetPathTemplate() + if err != nil { + return err + } + for _, method := range methods { + if err := fn(RouteInfo{Method: method, Path: path}); err != nil { + return err + } + } + return nil + }) +} + // Route registers an HTTP router. func (s *Server) Route(prefix string, filters ...FilterFunc) *Router { return newRouter(prefix, s, filters...) @@ -208,14 +239,15 @@ func (s *Server) filter() mux.MiddlewareFunc { } tr := &Transport{ - endpoint: s.endpoint.String(), operation: pathTemplate, + pathTemplate: pathTemplate, reqHeader: headerCarrier(req.Header), replyHeader: headerCarrier(w.Header()), request: req, - pathTemplate: pathTemplate, } - + if s.endpoint != nil { + tr.endpoint = s.endpoint.String() + } tr.request = req.WithContext(transport.NewServerContext(ctx, tr)) next.ServeHTTP(w, tr.request) }) @@ -227,16 +259,16 @@ func (s *Server) filter() mux.MiddlewareFunc { // https://127.0.0.1:8000 // Legacy: http://127.0.0.1:8000?isSecure=false func (s *Server) Endpoint() (*url.URL, error) { - if s.err != nil { - return nil, s.err + if err := s.listenAndEndpoint(); err != nil { + return nil, err } return s.endpoint, nil } // Start start the HTTP server. func (s *Server) Start(ctx context.Context) error { - if s.err != nil { - return s.err + if err := s.listenAndEndpoint(); err != nil { + return err } s.BaseContext = func(net.Listener) context.Context { return ctx @@ -264,15 +296,18 @@ func (s *Server) listenAndEndpoint() error { if s.lis == nil { lis, err := net.Listen(s.network, s.address) if err != nil { + s.err = err return err } s.lis = lis } - addr, err := host.Extract(s.address, s.lis) - if err != nil { - _ = s.lis.Close() - return err + if s.endpoint == nil { + addr, err := host.Extract(s.address, s.lis) + if err != nil { + s.err = err + return err + } + s.endpoint = endpoint.NewEndpoint(endpoint.Scheme("http", s.tlsConf != nil), addr) } - s.endpoint = endpoint.NewEndpoint(endpoint.Scheme("http", s.tlsConf != nil), addr) - return nil + return s.err } diff --git a/transport/http/server_test.go b/transport/http/server_test.go index 34f2b8553..05bc8399e 100644 --- a/transport/http/server_test.go +++ b/transport/http/server_test.go @@ -14,25 +14,75 @@ import ( "time" "github.com/go-kratos/kratos/v2/errors" - "github.com/go-kratos/kratos/v2/middleware" "github.com/go-kratos/kratos/v2/internal/host" ) +var h = func(w http.ResponseWriter, r *http.Request) { + _ = json.NewEncoder(w).Encode(testData{Path: r.RequestURI}) +} + type testKey struct{} type testData struct { Path string `json:"path"` } -func TestServer(t *testing.T) { - fn := func(w http.ResponseWriter, r *http.Request) { - _ = json.NewEncoder(w).Encode(testData{Path: r.RequestURI}) +// handleFuncWrapper is a wrapper for http.HandlerFunc to implement http.Handler +type handleFuncWrapper struct { + fn http.HandlerFunc +} + +func (x *handleFuncWrapper) ServeHTTP(writer http.ResponseWriter, request *http.Request) { + x.fn.ServeHTTP(writer, request) +} + +func newHandleFuncWrapper(fn http.HandlerFunc) http.Handler { + return &handleFuncWrapper{fn: fn} +} + +func TestServeHTTP(t *testing.T) { + ln, err := net.Listen("tcp", ":0") + if err != nil { + t.Fatal(err) + } + mux := NewServer(Listener(ln)) + mux.HandleFunc("/index", h) + mux.Route("/errors").GET("/cause", func(ctx Context) error { + return errors.BadRequest("xxx", "zzz"). + WithMetadata(map[string]string{"foo": "bar"}). + WithCause(fmt.Errorf("error cause")) + }) + if err = mux.WalkRoute(func(r RouteInfo) error { + t.Logf("WalkRoute: %+v", r) + return nil + }); err != nil { + t.Fatal(err) + } + if e, err := mux.Endpoint(); err != nil || e == nil || strings.HasSuffix(e.Host, ":0") { + t.Fatal(e, err) + } + srv := http.Server{Handler: mux} + go func() { + if err := srv.Serve(ln); err != nil { + if errors.Is(err, http.ErrServerClosed) { + return + } + panic(err) + } + }() + time.Sleep(time.Second) + if err := srv.Shutdown(context.Background()); err != nil { + t.Log(err) } +} + +func TestServer(t *testing.T) { ctx := context.Background() srv := NewServer() - srv.HandleFunc("/index", fn) - srv.HandleFunc("/index/{id:[0-9]+}", fn) + srv.Handle("/index", newHandleFuncWrapper(h)) + srv.HandleFunc("/index/{id:[0-9]+}", h) + srv.HandlePrefix("/test/prefix", newHandleFuncWrapper(h)) srv.HandleHeader("content-type", "application/grpc-web+json", func(w http.ResponseWriter, r *http.Request) { _ = json.NewEncoder(w).Encode(testData{Path: r.RequestURI}) }) @@ -55,6 +105,7 @@ func TestServer(t *testing.T) { testHeader(t, srv) testClient(t, srv) testAccept(t, srv) + time.Sleep(time.Second) if srv.Stop(ctx) != nil { t.Errorf("expected nil got %v", srv.Stop(ctx)) } @@ -121,20 +172,21 @@ func testClient(t *testing.T, srv *Server) { path string code int }{ - {"GET", "/index", 200}, - {"PUT", "/index", 200}, - {"POST", "/index", 200}, - {"PATCH", "/index", 200}, - {"DELETE", "/index", 200}, + {"GET", "/index", http.StatusOK}, + {"PUT", "/index", http.StatusOK}, + {"POST", "/index", http.StatusOK}, + {"PATCH", "/index", http.StatusOK}, + {"DELETE", "/index", http.StatusOK}, - {"GET", "/index/1", 200}, - {"PUT", "/index/1", 200}, - {"POST", "/index/1", 200}, - {"PATCH", "/index/1", 200}, - {"DELETE", "/index/1", 200}, + {"GET", "/index/1", http.StatusOK}, + {"PUT", "/index/1", http.StatusOK}, + {"POST", "/index/1", http.StatusOK}, + {"PATCH", "/index/1", http.StatusOK}, + {"DELETE", "/index/1", http.StatusOK}, - {"GET", "/index/notfound", 404}, - {"GET", "/errors/cause", 400}, + {"GET", "/index/notfound", http.StatusNotFound}, + {"GET", "/errors/cause", http.StatusBadRequest}, + {"GET", "/test/prefix/123111", http.StatusOK}, } e, err := srv.Endpoint() if err != nil { @@ -260,17 +312,6 @@ func TestLogger(t *testing.T) { // todo } -func TestMiddleware(t *testing.T) { - o := &Server{} - v := []middleware.Middleware{ - func(middleware.Handler) middleware.Handler { return nil }, - } - Middleware(v...)(o) - if !reflect.DeepEqual(v, o.ms) { - t.Errorf("expected %v got %v", v, o.ms) - } -} - func TestRequestDecoder(t *testing.T) { o := &Server{} v := func(*http.Request, interface{}) error { return nil } @@ -307,11 +348,26 @@ func TestTLSConfig(t *testing.T) { } } +func TestStrictSlash(t *testing.T) { + o := &Server{} + v := true + StrictSlash(v)(o) + if !reflect.DeepEqual(v, o.strictSlash) { + t.Errorf("expected %v got %v", v, o.tlsConf) + } +} + func TestListener(t *testing.T) { - lis := &net.TCPListener{} + lis, err := net.Listen("tcp", ":0") + if err != nil { + t.Fatal(err) + } s := &Server{} Listener(lis)(s) if !reflect.DeepEqual(s.lis, lis) { t.Errorf("expected %v got %v", lis, s.lis) } + if e, err := s.Endpoint(); err != nil || e == nil { + t.Errorf("expected not empty") + } } diff --git a/transport/transport_test.go b/transport/transport_test.go index c29c51e4b..b83c8ee3c 100644 --- a/transport/transport_test.go +++ b/transport/transport_test.go @@ -55,6 +55,9 @@ func TestServerTransport(t *testing.T) { if mtr == nil { t.Errorf("expected:%v got:%v", nil, mtr) } + if mtr.Kind().String() != KindGRPC.String() { + t.Errorf("expected:%v got:%v", KindGRPC.String(), mtr.Kind().String()) + } if !reflect.DeepEqual(mtr.endpoint, "test_endpoint") { t.Errorf("expected:%v got:%v", "test_endpoint", mtr.endpoint) } diff --git a/version.go b/version.go index 3a3d022e1..08f05950c 100644 --- a/version.go +++ b/version.go @@ -1,4 +1,4 @@ package kratos // Release is the current kratos version. -const Release = "v2.3.1" +const Release = "v2.4.1"