From bb12b0ae55cc6cb361065e570386d51ed95edde8 Mon Sep 17 00:00:00 2001 From: Paulo Pereira Date: Sat, 29 Aug 2020 19:50:52 +0100 Subject: [PATCH 1/8] proto uploads and multi projects proto --- .github/workflows/integration-test.yml | 9 + .gitignore | 3 + Dockerfile | 8 +- Gopkg.toml | 58 --- Readme.md | 78 +++ example/multi-files/entrypoint.sh | 4 +- example/multi-files/stub/greet2.json | 2 +- example/multi-package/entrypoint.sh | 4 +- example/multi-project/client/main.go | 38 ++ example/multi-project/entrypoint.sh | 7 + .../multi-project/proto/prj-bar/bar/bar.pb.go | 142 +++++ .../multi-project/proto/prj-bar/bar/bar.proto | 9 + .../multi-project/proto/prj-foo/foo/foo.pb.go | 152 ++++++ .../multi-project/proto/prj-foo/foo/foo.proto | 10 + .../proto/prj-foo/foo/hello.pb.go | 158 ++++++ .../proto/prj-foo/foo/hello.proto | 12 + example/multi-project/stub/simple.json | 15 + example/simple/entrypoint.sh | 4 +- example/stream/entrypoint.sh | 4 +- example/upload/client/bar/bar.pb.go | 142 +++++ example/upload/client/foo/foo.pb.go | 153 ++++++ example/upload/client/foo/hello.pb.go | 158 ++++++ example/upload/client/main.go | 50 ++ example/upload/entrypoint.sh | 7 + example/upload/proto/bar/bar.proto | 9 + example/upload/proto/foo/foo.proto | 10 + example/upload/proto/foo/hello.proto | 12 + example/upload/stub/simple.json | 15 + example/well_known_types/entrypoint.sh | 4 +- go.mod | 16 + go.sum | 239 +++++++++ gripmock.go | 493 +++++++++++++++--- protoc-gen-gripmock/generator.go | 44 +- protoc-gen-gripmock/server.tmpl | 6 +- stub/stub.go | 35 +- tool/tool.go | 96 ++++ 36 files changed, 2016 insertions(+), 190 deletions(-) delete mode 100644 Gopkg.toml create mode 100644 example/multi-project/client/main.go create mode 100755 example/multi-project/entrypoint.sh create mode 100644 example/multi-project/proto/prj-bar/bar/bar.pb.go create mode 100644 example/multi-project/proto/prj-bar/bar/bar.proto create mode 100644 example/multi-project/proto/prj-foo/foo/foo.pb.go create mode 100644 example/multi-project/proto/prj-foo/foo/foo.proto create mode 100644 example/multi-project/proto/prj-foo/foo/hello.pb.go create mode 100644 example/multi-project/proto/prj-foo/foo/hello.proto create mode 100644 example/multi-project/stub/simple.json create mode 100644 example/upload/client/bar/bar.pb.go create mode 100644 example/upload/client/foo/foo.pb.go create mode 100644 example/upload/client/foo/hello.pb.go create mode 100644 example/upload/client/main.go create mode 100755 example/upload/entrypoint.sh create mode 100644 example/upload/proto/bar/bar.proto create mode 100644 example/upload/proto/foo/foo.proto create mode 100644 example/upload/proto/foo/hello.proto create mode 100644 example/upload/stub/simple.json create mode 100644 go.mod create mode 100644 go.sum create mode 100644 tool/tool.go diff --git a/.github/workflows/integration-test.yml b/.github/workflows/integration-test.yml index e4d4c8de..5f7c267f 100644 --- a/.github/workflows/integration-test.yml +++ b/.github/workflows/integration-test.yml @@ -34,4 +34,13 @@ jobs: uses: ./ with: entrypoint: example/multi-files/entrypoint.sh + - name: Run upload Example + uses: ./ + with: + entrypoint: example/upload/entrypoint.sh + - name: Run multi project Example + uses: ./ + with: + entrypoint: example/multi-project/entrypoint.sh + diff --git a/.gitignore b/.gitignore index 9028d268..42e5b0cf 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ vendor Gopkg.lock gripmock + +# IDEs +.vscode diff --git a/Dockerfile b/Dockerfile index 9f105cf5..e53be36b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,7 @@ FROM golang:alpine +ENV GO111MODULE=off + RUN mkdir /proto RUN mkdir /stubs @@ -12,7 +14,9 @@ RUN go get -u -v github.com/golang/protobuf/protoc-gen-go \ golang.org/x/net/context \ github.com/go-chi/chi \ github.com/lithammer/fuzzysearch/fuzzy \ - golang.org/x/tools/imports + golang.org/x/tools/imports \ + google.golang.org/protobuf/reflect/protoreflect \ + google.golang.org/protobuf/runtime/protoimpl RUN go get -u -v github.com/gobuffalo/packr/v2/... \ github.com/gobuffalo/packr/v2/packr2 @@ -45,6 +49,6 @@ WORKDIR /go/src/github.com/tokopedia/gripmock # install gripmock RUN go install -v -EXPOSE 4770 4771 +EXPOSE 4770 4771 4772 ENTRYPOINT ["gripmock"] diff --git a/Gopkg.toml b/Gopkg.toml deleted file mode 100644 index 93e31bc3..00000000 --- a/Gopkg.toml +++ /dev/null @@ -1,58 +0,0 @@ -# Gopkg.toml example -# -# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md -# for detailed Gopkg.toml documentation. -# -# required = ["github.com/user/thing/cmd/thing"] -# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"] -# -# [[constraint]] -# name = "github.com/user/project" -# version = "1.0.0" -# -# [[constraint]] -# name = "github.com/user/project2" -# branch = "dev" -# source = "github.com/myfork/project2" -# -# [[override]] -# name = "github.com/x/y" -# version = "2.4.0" -# -# [prune] -# non-go = false -# go-tests = true -# unused-packages = true - - -[[constraint]] - branch = "master" - name = "github.com/alecthomas/participle" - -[[constraint]] - name = "github.com/go-chi/chi" - version = "3.3.2" - -[[constraint]] - name = "github.com/golang/protobuf" - version = "1.0.0" - -[[constraint]] - name = "github.com/lithammer/fuzzysearch" - version = "1.0.0" - -[[constraint]] - name = "github.com/stretchr/testify" - version = "1.2.1" - -[[constraint]] - branch = "master" - name = "golang.org/x/net" - -[[constraint]] - name = "google.golang.org/grpc" - version = "1.11.3" - -[prune] - go-tests = true - unused-packages = true diff --git a/Readme.md b/Readme.md index 44845a05..c3bad716 100644 --- a/Readme.md +++ b/Readme.md @@ -140,3 +140,81 @@ Input matching has 3 rules to match an input. which is **equals**,**contains** a } ``` +## After Fork +The above is still valid. + +The reason I created a fork was because I did a lot of changes and ended up drifting a bit from the original project. + +## New features + +This fork adds the following features: +* Packages at the same Level - proto files defined at their own folders and none of them is at the min package (top level) +* Immediate sub directories can represent different imports +* Uploading proto files + +Changes: +* modules +* generating proto into GOPATH + +Breaking changes: +* It no longer accepts individual proto files, but rather directories with the proto files +* `GOPATH` needs to be defined + +### Packages at the same Level +Now we can address the scenario where we have proto files packages where none is at the main package. +Imagine that we have two projects. One is `foo` and the other is `bar`. `foo` has a dependency on `bar`. +We should do the following. +Create a folder that will have all proto files (eg: proto) and then underneath the folders for the proto files for `foo` and another for `bar`. + +We will end up with: + +``` +proto +├── bar +│   └── bar.proto +└── foo + ├── foo.proto + └── hello.proto +``` + +Unfortunately just copying doesn't work for all projects, since some projects have their own particularities and a generic tool would have a hard time trying to cope with all of them. +I am thinking about when some projects define the packages in the `protoc` command like `--go_out=Mbar/bar.proto=this/is/a/package:.`. +What we have to do is the opposite. Change the proto files used for the mocks so that all have the option `go_package` and that the imports reflect the current structure. + +If sub dirs import flag is set `-isb`, all immediate sub dirs from the list of dirs passed as argument, like `foo` and `bar` inside `/proto`, +will be imported by the `protoc` with the compile option `-I` allowing us to have different packages in different sub directories. + +Make sure that: +* all proto files have the `option go_package`. + * eg: `option go_package = "github.com/quintans/foo";` +* all the imports must be relative to the current file structure + * eg: `import "github.com/quintans/bar/bar.proto";` ==> `import "bar/bar.proto";` +* do not use `.` in the package name. + * eg: `svc.foo` => `foo`. It is best to make it consistent with the folder name. + +Finally start gripmock specifying the top level proto folder +```sh +gripmock -o ./grpc ./proto +``` + +or without an initial proto folder + +```sh +gripmock -o ./grpc +``` + +Docker +```sh +docker run -p 4770:4770 -p 4771:4771 -p 4772:4772 -v /mypath:/proto -v /mystubs:/stub tkpd/gripmock --stub=/stub /proto +``` + +or without an initial proto folder or stubs + +```sh +docker run -p 4770:4770 -p 4771:4771 -p 4772:4772 tkpd/gripmock +``` + +### Uploading proto files +Proto files can be uploaded by zipping the proto folder and upload it to `http://localhost:4772/upload`. There is also an utility tool at the `tool` package. + +This will add up to the existing proto files. diff --git a/example/multi-files/entrypoint.sh b/example/multi-files/entrypoint.sh index 2a0245f2..cf47295c 100755 --- a/example/multi-files/entrypoint.sh +++ b/example/multi-files/entrypoint.sh @@ -2,6 +2,6 @@ # this file is used by .github/workflows/integration-test.yml -gripmock --stub=example/multi-files/stub example/multi-files/file1.proto example/multi-files/file2.proto & +gripmock --stub=example/multi-files/stub example/multi-files & -go run example/multi-files/client/*.go \ No newline at end of file +go run example/multi-files/client/*.go diff --git a/example/multi-files/stub/greet2.json b/example/multi-files/stub/greet2.json index e902237a..55598b1e 100644 --- a/example/multi-files/stub/greet2.json +++ b/example/multi-files/stub/greet2.json @@ -9,7 +9,7 @@ "output": { "data": { "message": "Hello Tokopedia 2", - "return_code": 1 + "return_code": 2 } } } diff --git a/example/multi-package/entrypoint.sh b/example/multi-package/entrypoint.sh index eef301c6..d7de2f06 100755 --- a/example/multi-package/entrypoint.sh +++ b/example/multi-package/entrypoint.sh @@ -4,6 +4,6 @@ # we need to add example/multi-package ar as included import path # without it protoc could not find the bar/bar.proto -gripmock --stub=example/multi-package/stub --imports=example/multi-package/ example/multi-package/foo.proto example/multi-package/hello.proto & +gripmock --stub=example/multi-package/stub example/multi-package & -go run example/multi-package/client/*.go \ No newline at end of file +go run example/multi-package/client/*.go diff --git a/example/multi-project/client/main.go b/example/multi-project/client/main.go new file mode 100644 index 00000000..b6e10ece --- /dev/null +++ b/example/multi-project/client/main.go @@ -0,0 +1,38 @@ +package main + +import ( + "context" + "log" + "os" + "time" + + "github.com/tokopedia/gripmock/example/multi-project/proto/prj-bar/bar" + "github.com/tokopedia/gripmock/example/multi-project/proto/prj-foo/foo" + "google.golang.org/grpc" +) + +func main() { + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + + // Set up a connection to the server. + conn, err := grpc.DialContext(ctx, "localhost:4770", grpc.WithInsecure(), grpc.WithBlock()) + if err != nil { + log.Fatalf("did not connect: %v", err) + } + defer conn.Close() + + c := foo.NewGreeterClient(conn) + + // Contact the server and print out its response. + name := "tokopedia" + if len(os.Args) > 1 { + name = os.Args[1] + } + r, err := c.Greet(context.Background(), &bar.Request{Name: name}) + if err != nil { + log.Fatalf("error from grpc: %v", err) + } + log.Printf("Greeting: %s (return code %d)", r.Message, r.ReturnCode) + +} diff --git a/example/multi-project/entrypoint.sh b/example/multi-project/entrypoint.sh new file mode 100755 index 00000000..c65dd434 --- /dev/null +++ b/example/multi-project/entrypoint.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env sh + +# this file is used by .github/workflows/integration-test.yml + +gripmock --stub=example/multi-project/stub -isd example/multi-project/proto & + +go run example/multi-project/client/*.go diff --git a/example/multi-project/proto/prj-bar/bar/bar.pb.go b/example/multi-project/proto/prj-bar/bar/bar.pb.go new file mode 100644 index 00000000..832e74e2 --- /dev/null +++ b/example/multi-project/proto/prj-bar/bar/bar.pb.go @@ -0,0 +1,142 @@ +package bar + +import ( + reflect "reflect" + sync "sync" + + proto "github.com/golang/protobuf/proto" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +// This is a compile-time assertion that a sufficiently up-to-date version +// of the legacy proto package is being used. +const _ = proto.ProtoPackageIsVersion4 + +type Request struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` +} + +func (x *Request) Reset() { + *x = Request{} + if protoimpl.UnsafeEnabled { + mi := &file_bar_bar_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Request) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Request) ProtoMessage() {} + +func (x *Request) ProtoReflect() protoreflect.Message { + mi := &file_bar_bar_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Request.ProtoReflect.Descriptor instead. +func (*Request) Descriptor() ([]byte, []int) { + return file_bar_bar_proto_rawDescGZIP(), []int{0} +} + +func (x *Request) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +var File_bar_bar_proto protoreflect.FileDescriptor + +var file_bar_bar_proto_rawDesc = []byte{ + 0x0a, 0x0d, 0x62, 0x61, 0x72, 0x2f, 0x62, 0x61, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, + 0x03, 0x62, 0x61, 0x72, 0x22, 0x1d, 0x0a, 0x07, 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, 0x42, 0x16, 0x5a, 0x14, 0x67, 0x72, 0x69, 0x70, 0x6d, 0x6f, 0x63, 0x6b, 0x2f, + 0x70, 0x72, 0x6a, 0x2d, 0x62, 0x61, 0x72, 0x2f, 0x62, 0x61, 0x72, 0x62, 0x06, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x33, +} + +var ( + file_bar_bar_proto_rawDescOnce sync.Once + file_bar_bar_proto_rawDescData = file_bar_bar_proto_rawDesc +) + +func file_bar_bar_proto_rawDescGZIP() []byte { + file_bar_bar_proto_rawDescOnce.Do(func() { + file_bar_bar_proto_rawDescData = protoimpl.X.CompressGZIP(file_bar_bar_proto_rawDescData) + }) + return file_bar_bar_proto_rawDescData +} + +var file_bar_bar_proto_msgTypes = make([]protoimpl.MessageInfo, 1) +var file_bar_bar_proto_goTypes = []interface{}{ + (*Request)(nil), // 0: bar.Request +} +var file_bar_bar_proto_depIdxs = []int32{ + 0, // [0:0] is the sub-list for method output_type + 0, // [0:0] 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_bar_bar_proto_init() } +func file_bar_bar_proto_init() { + if File_bar_bar_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_bar_bar_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Request); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_bar_bar_proto_rawDesc, + NumEnums: 0, + NumMessages: 1, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_bar_bar_proto_goTypes, + DependencyIndexes: file_bar_bar_proto_depIdxs, + MessageInfos: file_bar_bar_proto_msgTypes, + }.Build() + File_bar_bar_proto = out.File + file_bar_bar_proto_rawDesc = nil + file_bar_bar_proto_goTypes = nil + file_bar_bar_proto_depIdxs = nil +} diff --git a/example/multi-project/proto/prj-bar/bar/bar.proto b/example/multi-project/proto/prj-bar/bar/bar.proto new file mode 100644 index 00000000..1385810c --- /dev/null +++ b/example/multi-project/proto/prj-bar/bar/bar.proto @@ -0,0 +1,9 @@ +syntax = "proto3"; + +option go_package="gripmock/prj-bar/bar"; + +package bar; + +message Request{ + string name = 1; +} diff --git a/example/multi-project/proto/prj-foo/foo/foo.pb.go b/example/multi-project/proto/prj-foo/foo/foo.pb.go new file mode 100644 index 00000000..9a0575ef --- /dev/null +++ b/example/multi-project/proto/prj-foo/foo/foo.pb.go @@ -0,0 +1,152 @@ +package foo + +import ( + reflect "reflect" + sync "sync" + + proto "github.com/golang/protobuf/proto" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +// This is a compile-time assertion that a sufficiently up-to-date version +// of the legacy proto package is being used. +const _ = proto.ProtoPackageIsVersion4 + +type Response struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"` + ReturnCode int32 `protobuf:"varint,2,opt,name=return_code,json=returnCode,proto3" json:"return_code,omitempty"` +} + +func (x *Response) Reset() { + *x = Response{} + if protoimpl.UnsafeEnabled { + mi := &file_foo_foo_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Response) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Response) ProtoMessage() {} + +func (x *Response) ProtoReflect() protoreflect.Message { + mi := &file_foo_foo_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Response.ProtoReflect.Descriptor instead. +func (*Response) Descriptor() ([]byte, []int) { + return file_foo_foo_proto_rawDescGZIP(), []int{0} +} + +func (x *Response) GetMessage() string { + if x != nil { + return x.Message + } + return "" +} + +func (x *Response) GetReturnCode() int32 { + if x != nil { + return x.ReturnCode + } + return 0 +} + +var File_foo_foo_proto protoreflect.FileDescriptor + +var file_foo_foo_proto_rawDesc = []byte{ + 0x0a, 0x0d, 0x66, 0x6f, 0x6f, 0x2f, 0x66, 0x6f, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, + 0x03, 0x66, 0x6f, 0x6f, 0x22, 0x45, 0x0a, 0x08, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 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, 0x12, 0x1f, 0x0a, 0x0b, 0x72, 0x65, + 0x74, 0x75, 0x72, 0x6e, 0x5f, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, + 0x0a, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x43, 0x6f, 0x64, 0x65, 0x42, 0x16, 0x5a, 0x14, 0x67, + 0x72, 0x69, 0x70, 0x6d, 0x6f, 0x63, 0x6b, 0x2f, 0x70, 0x72, 0x6a, 0x2d, 0x66, 0x6f, 0x6f, 0x2f, + 0x66, 0x6f, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_foo_foo_proto_rawDescOnce sync.Once + file_foo_foo_proto_rawDescData = file_foo_foo_proto_rawDesc +) + +func file_foo_foo_proto_rawDescGZIP() []byte { + file_foo_foo_proto_rawDescOnce.Do(func() { + file_foo_foo_proto_rawDescData = protoimpl.X.CompressGZIP(file_foo_foo_proto_rawDescData) + }) + return file_foo_foo_proto_rawDescData +} + +var file_foo_foo_proto_msgTypes = make([]protoimpl.MessageInfo, 1) +var file_foo_foo_proto_goTypes = []interface{}{ + (*Response)(nil), // 0: foo.Response +} +var file_foo_foo_proto_depIdxs = []int32{ + 0, // [0:0] is the sub-list for method output_type + 0, // [0:0] 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_foo_foo_proto_init() } +func file_foo_foo_proto_init() { + if File_foo_foo_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_foo_foo_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Response); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_foo_foo_proto_rawDesc, + NumEnums: 0, + NumMessages: 1, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_foo_foo_proto_goTypes, + DependencyIndexes: file_foo_foo_proto_depIdxs, + MessageInfos: file_foo_foo_proto_msgTypes, + }.Build() + File_foo_foo_proto = out.File + file_foo_foo_proto_rawDesc = nil + file_foo_foo_proto_goTypes = nil + file_foo_foo_proto_depIdxs = nil +} diff --git a/example/multi-project/proto/prj-foo/foo/foo.proto b/example/multi-project/proto/prj-foo/foo/foo.proto new file mode 100644 index 00000000..8878651b --- /dev/null +++ b/example/multi-project/proto/prj-foo/foo/foo.proto @@ -0,0 +1,10 @@ +syntax = "proto3"; + +option go_package="gripmock/prj-foo/foo"; + +package foo; + +message Response { + string message = 1; + int32 return_code = 2; +} diff --git a/example/multi-project/proto/prj-foo/foo/hello.pb.go b/example/multi-project/proto/prj-foo/foo/hello.pb.go new file mode 100644 index 00000000..e820369e --- /dev/null +++ b/example/multi-project/proto/prj-foo/foo/hello.pb.go @@ -0,0 +1,158 @@ +package foo + +import ( + context "context" + reflect "reflect" + + proto "github.com/golang/protobuf/proto" + "github.com/tokopedia/gripmock/example/multi-project/proto/prj-bar/bar" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +// This is a compile-time assertion that a sufficiently up-to-date version +// of the legacy proto package is being used. +const _ = proto.ProtoPackageIsVersion4 + +var File_foo_hello_proto protoreflect.FileDescriptor + +var file_foo_hello_proto_rawDesc = []byte{ + 0x0a, 0x0f, 0x66, 0x6f, 0x6f, 0x2f, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x12, 0x03, 0x66, 0x6f, 0x6f, 0x1a, 0x0d, 0x62, 0x61, 0x72, 0x2f, 0x62, 0x61, 0x72, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x0d, 0x66, 0x6f, 0x6f, 0x2f, 0x66, 0x6f, 0x6f, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x32, 0x2f, 0x0a, 0x07, 0x47, 0x72, 0x65, 0x65, 0x74, 0x65, 0x72, 0x12, + 0x24, 0x0a, 0x05, 0x47, 0x72, 0x65, 0x65, 0x74, 0x12, 0x0c, 0x2e, 0x62, 0x61, 0x72, 0x2e, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0d, 0x2e, 0x66, 0x6f, 0x6f, 0x2e, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x16, 0x5a, 0x14, 0x67, 0x72, 0x69, 0x70, 0x6d, 0x6f, 0x63, + 0x6b, 0x2f, 0x70, 0x72, 0x6a, 0x2d, 0x66, 0x6f, 0x6f, 0x2f, 0x66, 0x6f, 0x6f, 0x62, 0x06, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var file_foo_hello_proto_goTypes = []interface{}{ + (*bar.Request)(nil), // 0: bar.Request + (*Response)(nil), // 1: foo.Response +} +var file_foo_hello_proto_depIdxs = []int32{ + 0, // 0: foo.Greeter.Greet:input_type -> bar.Request + 1, // 1: foo.Greeter.Greet:output_type -> foo.Response + 1, // [1:2] is the sub-list for method output_type + 0, // [0:1] 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_foo_hello_proto_init() } +func file_foo_hello_proto_init() { + if File_foo_hello_proto != nil { + return + } + file_foo_foo_proto_init() + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_foo_hello_proto_rawDesc, + NumEnums: 0, + NumMessages: 0, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_foo_hello_proto_goTypes, + DependencyIndexes: file_foo_hello_proto_depIdxs, + }.Build() + File_foo_hello_proto = out.File + file_foo_hello_proto_rawDesc = nil + file_foo_hello_proto_goTypes = nil + file_foo_hello_proto_depIdxs = nil +} + +// Reference imports to suppress errors if they are not otherwise used. +var _ context.Context +var _ grpc.ClientConnInterface + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +const _ = grpc.SupportPackageIsVersion6 + +// GreeterClient is the client API for Greeter service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. +type GreeterClient interface { + Greet(ctx context.Context, in *bar.Request, opts ...grpc.CallOption) (*Response, error) +} + +type greeterClient struct { + cc grpc.ClientConnInterface +} + +func NewGreeterClient(cc grpc.ClientConnInterface) GreeterClient { + return &greeterClient{cc} +} + +func (c *greeterClient) Greet(ctx context.Context, in *bar.Request, opts ...grpc.CallOption) (*Response, error) { + out := new(Response) + err := c.cc.Invoke(ctx, "/foo.Greeter/Greet", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// GreeterServer is the server API for Greeter service. +type GreeterServer interface { + Greet(context.Context, *bar.Request) (*Response, error) +} + +// UnimplementedGreeterServer can be embedded to have forward compatible implementations. +type UnimplementedGreeterServer struct { +} + +func (*UnimplementedGreeterServer) Greet(context.Context, *bar.Request) (*Response, error) { + return nil, status.Errorf(codes.Unimplemented, "method Greet not implemented") +} + +func RegisterGreeterServer(s *grpc.Server, srv GreeterServer) { + s.RegisterService(&_Greeter_serviceDesc, srv) +} + +func _Greeter_Greet_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(bar.Request) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(GreeterServer).Greet(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/foo.Greeter/Greet", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(GreeterServer).Greet(ctx, req.(*bar.Request)) + } + return interceptor(ctx, in, info, handler) +} + +var _Greeter_serviceDesc = grpc.ServiceDesc{ + ServiceName: "foo.Greeter", + HandlerType: (*GreeterServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "Greet", + Handler: _Greeter_Greet_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "foo/hello.proto", +} diff --git a/example/multi-project/proto/prj-foo/foo/hello.proto b/example/multi-project/proto/prj-foo/foo/hello.proto new file mode 100644 index 00000000..fa08c476 --- /dev/null +++ b/example/multi-project/proto/prj-foo/foo/hello.proto @@ -0,0 +1,12 @@ +syntax = "proto3"; + +option go_package="gripmock/prj-foo/foo"; + +package foo; + +import "bar/bar.proto"; +import "foo/foo.proto"; + +service Greeter { + rpc Greet (bar.Request) returns (Response); +} diff --git a/example/multi-project/stub/simple.json b/example/multi-project/stub/simple.json new file mode 100644 index 00000000..a5a19102 --- /dev/null +++ b/example/multi-project/stub/simple.json @@ -0,0 +1,15 @@ +{ + "service": "Greeter", + "method": "Greet", + "input": { + "equals": { + "name": "tokopedia" + } + }, + "output": { + "data": { + "message": "Hello Tokopedia", + "return_code": 1 + } + } +} diff --git a/example/simple/entrypoint.sh b/example/simple/entrypoint.sh index 120cc232..bd006981 100755 --- a/example/simple/entrypoint.sh +++ b/example/simple/entrypoint.sh @@ -2,6 +2,6 @@ # this file is used by .github/workflows/integration-test.yml -gripmock --stub=example/simple/stub example/simple/simple.proto & +gripmock --stub=example/simple/stub example/simple & -go run example/simple/client/*.go \ No newline at end of file +go run example/simple/client/*.go diff --git a/example/stream/entrypoint.sh b/example/stream/entrypoint.sh index 886aab5d..18978543 100755 --- a/example/stream/entrypoint.sh +++ b/example/stream/entrypoint.sh @@ -2,6 +2,6 @@ # this file is used by .github/workflows/integration-test.yml -gripmock --stub=example/stream/stub example/stream/stream.proto & +gripmock --stub=example/stream/stub example/stream & -go run example/stream/client/*.go \ No newline at end of file +go run example/stream/client/*.go diff --git a/example/upload/client/bar/bar.pb.go b/example/upload/client/bar/bar.pb.go new file mode 100644 index 00000000..a5beb5aa --- /dev/null +++ b/example/upload/client/bar/bar.pb.go @@ -0,0 +1,142 @@ +package bar + +import ( + reflect "reflect" + sync "sync" + + proto "github.com/golang/protobuf/proto" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +// This is a compile-time assertion that a sufficiently up-to-date version +// of the legacy proto package is being used. +const _ = proto.ProtoPackageIsVersion4 + +type Request struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` +} + +func (x *Request) Reset() { + *x = Request{} + if protoimpl.UnsafeEnabled { + mi := &file_bar_bar_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Request) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Request) ProtoMessage() {} + +func (x *Request) ProtoReflect() protoreflect.Message { + mi := &file_bar_bar_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Request.ProtoReflect.Descriptor instead. +func (*Request) Descriptor() ([]byte, []int) { + return file_bar_bar_proto_rawDescGZIP(), []int{0} +} + +func (x *Request) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +var File_bar_bar_proto protoreflect.FileDescriptor + +var file_bar_bar_proto_rawDesc = []byte{ + 0x0a, 0x0d, 0x62, 0x61, 0x72, 0x2f, 0x62, 0x61, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, + 0x03, 0x62, 0x61, 0x72, 0x22, 0x1d, 0x0a, 0x07, 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, 0x42, 0x20, 0x5a, 0x1e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, + 0x6d, 0x2f, 0x6b, 0x61, 0x72, 0x68, 0x6f, 0x6f, 0x2f, 0x67, 0x72, 0x69, 0x70, 0x6d, 0x6f, 0x63, + 0x6b, 0x2f, 0x62, 0x61, 0x72, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_bar_bar_proto_rawDescOnce sync.Once + file_bar_bar_proto_rawDescData = file_bar_bar_proto_rawDesc +) + +func file_bar_bar_proto_rawDescGZIP() []byte { + file_bar_bar_proto_rawDescOnce.Do(func() { + file_bar_bar_proto_rawDescData = protoimpl.X.CompressGZIP(file_bar_bar_proto_rawDescData) + }) + return file_bar_bar_proto_rawDescData +} + +var file_bar_bar_proto_msgTypes = make([]protoimpl.MessageInfo, 1) +var file_bar_bar_proto_goTypes = []interface{}{ + (*Request)(nil), // 0: bar.Request +} +var file_bar_bar_proto_depIdxs = []int32{ + 0, // [0:0] is the sub-list for method output_type + 0, // [0:0] 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_bar_bar_proto_init() } +func file_bar_bar_proto_init() { + if File_bar_bar_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_bar_bar_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Request); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_bar_bar_proto_rawDesc, + NumEnums: 0, + NumMessages: 1, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_bar_bar_proto_goTypes, + DependencyIndexes: file_bar_bar_proto_depIdxs, + MessageInfos: file_bar_bar_proto_msgTypes, + }.Build() + File_bar_bar_proto = out.File + file_bar_bar_proto_rawDesc = nil + file_bar_bar_proto_goTypes = nil + file_bar_bar_proto_depIdxs = nil +} diff --git a/example/upload/client/foo/foo.pb.go b/example/upload/client/foo/foo.pb.go new file mode 100644 index 00000000..2274c4cd --- /dev/null +++ b/example/upload/client/foo/foo.pb.go @@ -0,0 +1,153 @@ +package foo + +import ( + reflect "reflect" + sync "sync" + + proto "github.com/golang/protobuf/proto" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +// This is a compile-time assertion that a sufficiently up-to-date version +// of the legacy proto package is being used. +const _ = proto.ProtoPackageIsVersion4 + +type Response struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"` + ReturnCode int32 `protobuf:"varint,2,opt,name=return_code,json=returnCode,proto3" json:"return_code,omitempty"` +} + +func (x *Response) Reset() { + *x = Response{} + if protoimpl.UnsafeEnabled { + mi := &file_foo_foo_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Response) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Response) ProtoMessage() {} + +func (x *Response) ProtoReflect() protoreflect.Message { + mi := &file_foo_foo_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Response.ProtoReflect.Descriptor instead. +func (*Response) Descriptor() ([]byte, []int) { + return file_foo_foo_proto_rawDescGZIP(), []int{0} +} + +func (x *Response) GetMessage() string { + if x != nil { + return x.Message + } + return "" +} + +func (x *Response) GetReturnCode() int32 { + if x != nil { + return x.ReturnCode + } + return 0 +} + +var File_foo_foo_proto protoreflect.FileDescriptor + +var file_foo_foo_proto_rawDesc = []byte{ + 0x0a, 0x0d, 0x66, 0x6f, 0x6f, 0x2f, 0x66, 0x6f, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, + 0x03, 0x66, 0x6f, 0x6f, 0x22, 0x45, 0x0a, 0x08, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 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, 0x12, 0x1f, 0x0a, 0x0b, 0x72, 0x65, + 0x74, 0x75, 0x72, 0x6e, 0x5f, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, + 0x0a, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x43, 0x6f, 0x64, 0x65, 0x42, 0x20, 0x5a, 0x1e, 0x67, + 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6b, 0x61, 0x72, 0x68, 0x6f, 0x6f, + 0x2f, 0x67, 0x72, 0x69, 0x70, 0x6d, 0x6f, 0x63, 0x6b, 0x2f, 0x66, 0x6f, 0x6f, 0x62, 0x06, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_foo_foo_proto_rawDescOnce sync.Once + file_foo_foo_proto_rawDescData = file_foo_foo_proto_rawDesc +) + +func file_foo_foo_proto_rawDescGZIP() []byte { + file_foo_foo_proto_rawDescOnce.Do(func() { + file_foo_foo_proto_rawDescData = protoimpl.X.CompressGZIP(file_foo_foo_proto_rawDescData) + }) + return file_foo_foo_proto_rawDescData +} + +var file_foo_foo_proto_msgTypes = make([]protoimpl.MessageInfo, 1) +var file_foo_foo_proto_goTypes = []interface{}{ + (*Response)(nil), // 0: foo.Response +} +var file_foo_foo_proto_depIdxs = []int32{ + 0, // [0:0] is the sub-list for method output_type + 0, // [0:0] 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_foo_foo_proto_init() } +func file_foo_foo_proto_init() { + if File_foo_foo_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_foo_foo_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Response); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_foo_foo_proto_rawDesc, + NumEnums: 0, + NumMessages: 1, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_foo_foo_proto_goTypes, + DependencyIndexes: file_foo_foo_proto_depIdxs, + MessageInfos: file_foo_foo_proto_msgTypes, + }.Build() + File_foo_foo_proto = out.File + file_foo_foo_proto_rawDesc = nil + file_foo_foo_proto_goTypes = nil + file_foo_foo_proto_depIdxs = nil +} diff --git a/example/upload/client/foo/hello.pb.go b/example/upload/client/foo/hello.pb.go new file mode 100644 index 00000000..79fd29b9 --- /dev/null +++ b/example/upload/client/foo/hello.pb.go @@ -0,0 +1,158 @@ +package foo + +import ( + context "context" + reflect "reflect" + + proto "github.com/golang/protobuf/proto" + bar "github.com/tokopedia/gripmock/example/upload/client/bar" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +// This is a compile-time assertion that a sufficiently up-to-date version +// of the legacy proto package is being used. +const _ = proto.ProtoPackageIsVersion4 + +var File_foo_hello_proto protoreflect.FileDescriptor + +var file_foo_hello_proto_rawDesc = []byte{ + 0x0a, 0x0f, 0x66, 0x6f, 0x6f, 0x2f, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x12, 0x03, 0x66, 0x6f, 0x6f, 0x1a, 0x0d, 0x62, 0x61, 0x72, 0x2f, 0x62, 0x61, 0x72, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x0d, 0x66, 0x6f, 0x6f, 0x2f, 0x66, 0x6f, 0x6f, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x32, 0x2f, 0x0a, 0x07, 0x47, 0x72, 0x65, 0x65, 0x74, 0x65, 0x72, 0x12, + 0x24, 0x0a, 0x05, 0x47, 0x72, 0x65, 0x65, 0x74, 0x12, 0x0c, 0x2e, 0x62, 0x61, 0x72, 0x2e, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0d, 0x2e, 0x66, 0x6f, 0x6f, 0x2e, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x20, 0x5a, 0x1e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, + 0x63, 0x6f, 0x6d, 0x2f, 0x6b, 0x61, 0x72, 0x68, 0x6f, 0x6f, 0x2f, 0x67, 0x72, 0x69, 0x70, 0x6d, + 0x6f, 0x63, 0x6b, 0x2f, 0x66, 0x6f, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var file_foo_hello_proto_goTypes = []interface{}{ + (*bar.Request)(nil), // 0: bar.Request + (*Response)(nil), // 1: foo.Response +} +var file_foo_hello_proto_depIdxs = []int32{ + 0, // 0: foo.Greeter.Greet:input_type -> bar.Request + 1, // 1: foo.Greeter.Greet:output_type -> foo.Response + 1, // [1:2] is the sub-list for method output_type + 0, // [0:1] 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_foo_hello_proto_init() } +func file_foo_hello_proto_init() { + if File_foo_hello_proto != nil { + return + } + file_foo_foo_proto_init() + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_foo_hello_proto_rawDesc, + NumEnums: 0, + NumMessages: 0, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_foo_hello_proto_goTypes, + DependencyIndexes: file_foo_hello_proto_depIdxs, + }.Build() + File_foo_hello_proto = out.File + file_foo_hello_proto_rawDesc = nil + file_foo_hello_proto_goTypes = nil + file_foo_hello_proto_depIdxs = nil +} + +// Reference imports to suppress errors if they are not otherwise used. +var _ context.Context +var _ grpc.ClientConnInterface + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +const _ = grpc.SupportPackageIsVersion6 + +// GreeterClient is the client API for Greeter service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. +type GreeterClient interface { + Greet(ctx context.Context, in *bar.Request, opts ...grpc.CallOption) (*Response, error) +} + +type greeterClient struct { + cc grpc.ClientConnInterface +} + +func NewGreeterClient(cc grpc.ClientConnInterface) GreeterClient { + return &greeterClient{cc} +} + +func (c *greeterClient) Greet(ctx context.Context, in *bar.Request, opts ...grpc.CallOption) (*Response, error) { + out := new(Response) + err := c.cc.Invoke(ctx, "/foo.Greeter/Greet", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// GreeterServer is the server API for Greeter service. +type GreeterServer interface { + Greet(context.Context, *bar.Request) (*Response, error) +} + +// UnimplementedGreeterServer can be embedded to have forward compatible implementations. +type UnimplementedGreeterServer struct { +} + +func (*UnimplementedGreeterServer) Greet(context.Context, *bar.Request) (*Response, error) { + return nil, status.Errorf(codes.Unimplemented, "method Greet not implemented") +} + +func RegisterGreeterServer(s *grpc.Server, srv GreeterServer) { + s.RegisterService(&_Greeter_serviceDesc, srv) +} + +func _Greeter_Greet_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(bar.Request) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(GreeterServer).Greet(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/foo.Greeter/Greet", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(GreeterServer).Greet(ctx, req.(*bar.Request)) + } + return interceptor(ctx, in, info, handler) +} + +var _Greeter_serviceDesc = grpc.ServiceDesc{ + ServiceName: "foo.Greeter", + HandlerType: (*GreeterServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "Greet", + Handler: _Greeter_Greet_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "foo/hello.proto", +} diff --git a/example/upload/client/main.go b/example/upload/client/main.go new file mode 100644 index 00000000..c1a114df --- /dev/null +++ b/example/upload/client/main.go @@ -0,0 +1,50 @@ +package main + +import ( + "context" + "log" + "os" + "time" + + "github.com/tokopedia/gripmock/example/upload/client/bar" + "github.com/tokopedia/gripmock/example/upload/client/foo" + "github.com/tokopedia/gripmock/tool" + "google.golang.org/grpc" +) + +func main() { + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + + // upload proto files + _, err := tool.ZipFolderAndUpload("http://localhost:4772/upload", "example/upload/proto") + if err != nil { + log.Fatalf("did not upload proto: %v", err) + } + + _, err = tool.UploadJsonFile("http://localhost:4771/add", "example/upload/stub/simple.json") + if err != nil { + log.Fatalf("did not upload json: %v", err) + } + + // Set up a connection to the server. + conn, err := grpc.DialContext(ctx, "localhost:4770", grpc.WithInsecure(), grpc.WithBlock()) + if err != nil { + log.Fatalf("did not connect: %v", err) + } + defer conn.Close() + + c := foo.NewGreeterClient(conn) + + // Contact the server and print out its response. + name := "tokopedia" + if len(os.Args) > 1 { + name = os.Args[1] + } + r, err := c.Greet(context.Background(), &bar.Request{Name: name}) + if err != nil { + log.Fatalf("error from grpc: %v", err) + } + log.Printf("Greeting: %s (return code %d)", r.Message, r.ReturnCode) + +} diff --git a/example/upload/entrypoint.sh b/example/upload/entrypoint.sh new file mode 100755 index 00000000..76742ba3 --- /dev/null +++ b/example/upload/entrypoint.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env sh + +# this file is used by .github/workflows/integration-test.yml + +gripmock & + +go run example/upload/client/*.go diff --git a/example/upload/proto/bar/bar.proto b/example/upload/proto/bar/bar.proto new file mode 100644 index 00000000..adc6ec5a --- /dev/null +++ b/example/upload/proto/bar/bar.proto @@ -0,0 +1,9 @@ +syntax = "proto3"; + +option go_package="github.com/tokopedia/gripmock/bar"; + +package bar; + +message Request{ + string name = 1; +} diff --git a/example/upload/proto/foo/foo.proto b/example/upload/proto/foo/foo.proto new file mode 100644 index 00000000..d88f0657 --- /dev/null +++ b/example/upload/proto/foo/foo.proto @@ -0,0 +1,10 @@ +syntax = "proto3"; + +option go_package="github.com/tokopedia/gripmock/foo"; + +package foo; + +message Response { + string message = 1; + int32 return_code = 2; +} diff --git a/example/upload/proto/foo/hello.proto b/example/upload/proto/foo/hello.proto new file mode 100644 index 00000000..72383cd3 --- /dev/null +++ b/example/upload/proto/foo/hello.proto @@ -0,0 +1,12 @@ +syntax = "proto3"; + +option go_package="github.com/tokopedia/gripmock/foo"; + +package foo; + +import "bar/bar.proto"; +import "foo/foo.proto"; + +service Greeter { + rpc Greet (bar.Request) returns (Response); +} diff --git a/example/upload/stub/simple.json b/example/upload/stub/simple.json new file mode 100644 index 00000000..a5a19102 --- /dev/null +++ b/example/upload/stub/simple.json @@ -0,0 +1,15 @@ +{ + "service": "Greeter", + "method": "Greet", + "input": { + "equals": { + "name": "tokopedia" + } + }, + "output": { + "data": { + "message": "Hello Tokopedia", + "return_code": 1 + } + } +} diff --git a/example/well_known_types/entrypoint.sh b/example/well_known_types/entrypoint.sh index 9c656083..aa3decc5 100755 --- a/example/well_known_types/entrypoint.sh +++ b/example/well_known_types/entrypoint.sh @@ -2,6 +2,6 @@ # this file is used by .github/workflows/integration-test.yml -gripmock --stub=example/well_known_types/stub example/well_known_types/wkt.proto & +gripmock --stub=example/well_known_types/stub example/well_known_types & -go run example/well_known_types/client/*.go \ No newline at end of file +go run example/well_known_types/client/*.go diff --git a/go.mod b/go.mod new file mode 100644 index 00000000..58f3aee9 --- /dev/null +++ b/go.mod @@ -0,0 +1,16 @@ +module github.com/tokopedia/gripmock + +go 1.15 + +require ( + github.com/go-chi/chi v4.1.2+incompatible + github.com/gobuffalo/packr/v2 v2.8.0 + github.com/golang/protobuf v1.4.2 + github.com/lithammer/fuzzysearch v1.1.0 + github.com/stretchr/testify v1.6.1 + golang.org/x/net v0.0.0-20200226121028-0de0cce0169b + golang.org/x/tools v0.0.0-20200308013534-11ec41452d41 + google.golang.org/genproto v0.0.0-20200825200019-8632dd797987 + google.golang.org/grpc v1.31.1 + google.golang.org/protobuf v1.25.0 +) diff --git a/go.sum b/go.sum new file mode 100644 index 00000000..f0869b75 --- /dev/null +++ b/go.sum @@ -0,0 +1,239 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= +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/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +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/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= +github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= +github.com/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/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/go-chi/chi v4.1.2+incompatible h1:fGFk2Gmi/YKXk0OmGfBh0WgmN3XB8lVnEyNz34tQRec= +github.com/go-chi/chi v4.1.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ= +github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/gobuffalo/logger v1.0.3 h1:YaXOTHNPCvkqqA7w05A4v0k2tCdpr+sgFlgINbQ6gqc= +github.com/gobuffalo/logger v1.0.3/go.mod h1:SoeejUwldiS7ZsyCBphOGURmWdwUFXs0J7TCjEhjKxM= +github.com/gobuffalo/packd v1.0.0 h1:6ERZvJHfe24rfFmA9OaoKBdC7+c9sydrytMg8SdFGBM= +github.com/gobuffalo/packd v1.0.0/go.mod h1:6VTc4htmJRFB7u1m/4LeMTWjFoYrUiBkU9Fdec9hrhI= +github.com/gobuffalo/packr/v2 v2.8.0 h1:IULGd15bQL59ijXLxEvA5wlMxsmx/ZkQv9T282zNVIY= +github.com/gobuffalo/packr/v2 v2.8.0/go.mod h1:PDk2k3vGevNE3SwVyVRgQCCXETC9SaONCNSXT1Q8M1g= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/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.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 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +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.5.0 h1:/QaMHBdZ26BB3SSst0Iwl10Epc+xhTquomWX0oZEB6w= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= +github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= +github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/karrick/godirwalk v1.15.3 h1:0a2pXOgtB16CqIqXTiT7+K9L73f74n/aNQUnH6Ortew= +github.com/karrick/godirwalk v1.15.3/go.mod h1:j4mkqPuvaLI8mp1DroR3P6ad7cyYd4c1qeJ3RV7ULlk= +github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s= +github.com/konsorten/go-windows-terminal-sequences v1.0.2/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/lithammer/fuzzysearch v1.1.0 h1:go9v8tLCrNTTlH42OAaq4eHFe81TDHEnlrMEb6R4f+A= +github.com/lithammer/fuzzysearch v1.1.0/go.mod h1:Bqx4wo8lTOFcJr3ckpY6HA9lEIOO0H5HrkJ5CsN56HQ= +github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/markbates/errx v1.1.0 h1:QDFeR+UP95dO12JgW+tgi2UVfo0V8YBHiUIOaeBPiEI= +github.com/markbates/errx v1.1.0/go.mod h1:PLa46Oex9KNbVDZhKel8v1OT7hD5JZ2eI7AHhA0wswc= +github.com/markbates/oncer v1.0.0 h1:E83IaVAHygyndzPimgUYJjbshhDTALZyXxvk9FOlQRY= +github.com/markbates/oncer v1.0.0/go.mod h1:Z59JA581E9GP6w96jai+TGqafHPW+cPfRxz2aSZ0mcI= +github.com/markbates/safe v1.0.1 h1:yjZkbvRM6IzKj9tlu/zMJLS0n/V351OZWRnF3QfaUxI= +github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= +github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= +github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= +github.com/rogpeppe/go-internal v1.5.2 h1:qLvObTrvO/XRCqmkKxUlOBc48bI3efyDuAZe25QiF0w= +github.com/rogpeppe/go-internal v1.5.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= +github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= +github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cobra v0.0.6/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= +github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= +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.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= +github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= +github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= +go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191122220453-ac88ee75c92c h1:/nJuwDLoL/zrqY6gf57vxC+Pi+pZ8bfhpPkicO5H7W4= +golang.org/x/crypto v0.0.0-20191122220453-ac88ee75c92c/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/mod v0.2.0 h1:KU7oHjnv3XNWfa5COkzUifxZmxp1TyI7ImMXqFxLwvQ= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-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-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b h1:0mm1VjtFUOIlE1SbDlwjYaDxZVDP2S5ou6y0gSgXHu8= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/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-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/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-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894 h1:Cz4ceDQGXuKRnVBDTS23GTn/pU5OE2C0WrNTOYK1Uuc= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e h1:FDhOuMEY4JVRztM/gsbk+IKUQ8kj74bxZrgw87eMMVc= +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/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200308013534-11ec41452d41 h1:9Di9iYgOt9ThCipBxChBVhgNipDoE5mxO84rQV7D0FE= +golang.org/x/tools v0.0.0-20200308013534-11ec41452d41/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +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 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20200825200019-8632dd797987 h1:PDIOdWxZ8eRizhKa1AAvY53xsvLB1cWorMjslvY3VA8= +google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.21.0/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.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.31.1 h1:SfXqXS5hkufcdZ/mHtYCh53P2b+92WQq/DZcKLgsFRs= +google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= +google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +gopkg.in/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 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= +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/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= +gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/gripmock.go b/gripmock.go index ccc4fe1f..383f3c5b 100644 --- a/gripmock.go +++ b/gripmock.go @@ -1,88 +1,200 @@ package main import ( + "archive/zip" + "bytes" + "context" "flag" "fmt" + "io" + "io/ioutil" "log" + "net/http" "os" "os/exec" "os/signal" + "path/filepath" "strings" "syscall" + "github.com/go-chi/chi" "github.com/tokopedia/gripmock/stub" ) +const ( + version = "v0.0.1" + binaryName = "grpcserver" +) + +var ( + upProtoFolder = "/temp/up-proto" + goPath = "" +) + func main() { - outputPointer := flag.String("o", "", "directory to output server.go. Default is $GOPATH/src/grpc/") + ver := flag.Bool("v", false, "returns the version") + output := flag.String("o", "", "directory to output server.go. Default is $GOPATH/src/grpc/") grpcPort := flag.String("grpc-port", "4770", "Port of gRPC tcp server") - grpcBindAddr := flag.String("grpc-listen", "", "Adress the gRPC server will bind to. Default to localhost, set to 0.0.0.0 to use from another machine") + grpcBindAddr := flag.String("grpc-listen", "", "Address the gRPC server will bind to. Default to localhost, set to 0.0.0.0 to use from another machine") adminport := flag.String("admin-port", "4771", "Port of stub admin server") - adminBindAddr := flag.String("admin-listen", "", "Adress the admin server will bind to. Default to localhost, set to 0.0.0.0 to use from another machine") + adminBindAddr := flag.String("admin-listen", "", "Address the admin server will bind to. Default to localhost, set to 0.0.0.0 to use from another machine") stubPath := flag.String("stub", "", "Path where the stub files are (Optional)") + upPort := flag.String("up-port", "4772", "Port of upload proto server") + upBindAddr := flag.String("up-listen", "", "Address the upload proto server will bind to. Default to localhost, set to 0.0.0.0 to use from another machine") imports := flag.String("imports", "/protobuf", "comma separated imports path. default path /protobuf is where gripmock Dockerfile install WKT protos") + importSubdirs := flag.Bool("isd", false, "Immediate sub dirs from the list of dirs passed as argument, will be imported") // for backwards compatibility - if os.Args[1] == "gripmock" { + if len(os.Args) > 1 && os.Args[1] == "gripmock" { os.Args = append(os.Args[:1], os.Args[2:]...) } - flag.Parse() - fmt.Println("Starting GripMock") - - output := *outputPointer - if output == "" { - if os.Getenv("GOPATH") == "" { - log.Fatal("output is not provided and GOPATH is empty") - } - output = os.Getenv("GOPATH") + "/src/grpc" + if *ver { + fmt.Println("version:", version) + return } + flag.Parse() + log.Println("Starting GripMock", version) + goPath = os.Getenv("GOPATH") + if goPath == "" { + log.Fatal("GOPATH is empty") + } + if *output == "" { + *output = os.Getenv("GOPATH") + "/src/grpc" + } + upProtoFolder = goPath + upProtoFolder // for safety - output += "/" - if _, err := os.Stat(output); os.IsNotExist(err) { - os.Mkdir(output, os.ModePerm) + *output += "/" + if _, err := os.Stat(*output); os.IsNotExist(err) { + os.Mkdir(*output, os.ModePerm) } - // run admin stub server - stub.RunStubServer(stub.Options{ - StubPath: *stubPath, - Port: *adminport, - BindAddr: *adminBindAddr, - }) - // parse proto files protoPaths := flag.Args() - - if len(protoPaths) == 0 { - log.Fatal("Need atleast one proto file") + err := os.RemoveAll(upProtoFolder) + if err != nil { + log.Fatalf("did not remove %s: %v", upProtoFolder, err) + } + err = os.MkdirAll(upProtoFolder, os.ModePerm) + if err != nil { + log.Fatalf("did not create %s: %v", upProtoFolder, err) } + protoPaths = append(protoPaths, upProtoFolder) importDirs := strings.Split(*imports, ",") + if len(importDirs) == 0 { + log.Fatal("No importable dirs") + } - // generate pb.go and grpc server based on proto - generateProtoc(protocParam{ - protoPath: protoPaths, - adminPort: *adminport, - grpcAddress: *grpcBindAddr, - grpcPort: *grpcPort, - output: output, - imports: importDirs, - }) + ctx, cancel := context.WithCancel(context.Background()) + srvrs := &servers{ + options: stub.Options{ + StubPath: *stubPath, + Port: *adminport, + BindAddr: *adminBindAddr, + }, + params: protocParam{ + protoPaths: protoPaths, + adminPort: *adminport, + grpcAddress: *grpcBindAddr, + grpcPort: *grpcPort, + imports: importDirs, + importSubdirs: *importSubdirs, + }, + grpcOutput: *output, + } - // build the server - buildServer(output, protoPaths) + err = srvrs.boot(ctx) + if err != nil { + log.Fatal(err) + } - // and run - run, runerr := runGrpcServer(output) + upDone := uploadServer(ctx, Options{ + BindAddr: *upBindAddr, + Port: *upPort, + Output: upProtoFolder, + }, srvrs) var term = make(chan os.Signal) signal.Notify(term, syscall.SIGTERM, syscall.SIGKILL, syscall.SIGINT) select { - case err := <-runerr: - log.Fatal(err) case <-term: - fmt.Println("Stopping gRPC Server") - run.Process.Kill() + log.Println("Signaled to shutdown") + cancel() + srvrs.shutdown() + <-upDone + } +} + +type servers struct { + options stub.Options + params protocParam + grpcOutput string + cancel context.CancelFunc + stubDone <-chan struct{} + grpcDone <-chan struct{} +} + +func (s *servers) boot(ctx context.Context) error { + protoPaths := expandDirs(s.params.protoPaths) + if len(protoPaths) == 0 { + log.Println("No proto files found. Skipping stub and grpc boot.") + return nil + } + + ctx, cancel := context.WithCancel(ctx) + s.cancel = cancel + + // clean output folder + err := os.RemoveAll(s.grpcOutput) + if err != nil { + return err + } + err = os.Mkdir(s.grpcOutput, os.ModePerm) + if err != nil { + return err + } + + // run admin stub server + s.stubDone = stub.RunStubServer(ctx, s.options) + + var importDirs []string + if s.params.importSubdirs { + importDirs = append(s.params.imports, expandImports(s.params.protoPaths)...) + } else { + importDirs = append(s.params.imports, s.params.protoPaths...) + } + + pp := protocParam{ + imports: importDirs, + adminPort: s.params.adminPort, + grpcAddress: s.params.grpcAddress, + grpcPort: s.params.grpcPort, + protoPaths: protoPaths, + } + + // generate pb.go and grpc server based on proto + err = generateProtoc(pp, s.grpcOutput) + if err != nil { + return err + } + + // build the server + err = buildServer(s.grpcOutput) + if err != nil { + return err + } + + // and run + s.grpcDone, err = runGrpcServer(ctx, s.grpcOutput) + return err +} + +func (s *servers) shutdown() { + if s.cancel != nil { + s.cancel() + <-s.stubDone + <-s.grpcDone } } @@ -93,77 +205,288 @@ func getProtoName(path string) string { } type protocParam struct { - protoPath []string - adminPort string - grpcAddress string - grpcPort string - output string - imports []string + protoPaths []string + adminPort string + grpcAddress string + grpcPort string + imports []string + importSubdirs bool } -func generateProtoc(param protocParam) { - protodirs := strings.Split(param.protoPath[0], "/") - protodir := "" - if len(protodirs) > 0 { - protodir = strings.Join(protodirs[:len(protodirs)-1], "/") + "/" - } - - args := []string{"-I", protodir} +func generateProtoc(param protocParam, output string) error { + src := goPath + "/src" + args := []string{} // include well-known-types for _, i := range param.imports { args = append(args, "-I", i) + log.Println("Importing", i) } - args = append(args, param.protoPath...) - args = append(args, "--go_out=plugins=grpc:"+param.output) + args = append(args, "--go_out=plugins=grpc:"+src) args = append(args, fmt.Sprintf("--gripmock_out=admin-port=%s,grpc-address=%s,grpc-port=%s:%s", - param.adminPort, param.grpcAddress, param.grpcPort, param.output)) + param.adminPort, param.grpcAddress, param.grpcPort, output)) + args = append(args, param.protoPaths...) protoc := exec.Command("protoc", args...) protoc.Stdout = os.Stdout protoc.Stderr = os.Stderr err := protoc.Run() if err != nil { - log.Fatal("Fail on protoc ", err) + return fmt.Errorf("Fail on protoc: %w", err) } - // change package to "main" on generated code - for _, proto := range param.protoPath { - protoname := getProtoName(proto) - sed := exec.Command("sed", "-i", `s/^package \w*$/package main/`, param.output+protoname+".pb.go") - sed.Stderr = os.Stderr - sed.Stdout = os.Stdout - err = sed.Run() - if err != nil { - log.Fatal("Fail on sed") + // change package to "main" on top level generated code + files, err := ioutil.ReadDir(src) + if err != nil { + log.Fatalf("Can't read dir for imports from %s. %v\n", output, err) + } + + for _, fi := range files { + name := fi.Name() + if fi.Mode().IsRegular() && strings.HasSuffix(name, ".pb.go") { + source := filepath.Join(src, name) + sed := exec.Command("sed", "-i", `s/^package \w*$/package main/`, source) + sed.Stderr = os.Stderr + sed.Stdout = os.Stdout + err = sed.Run() + if err != nil { + return fmt.Errorf("Fail on sed: %w", err) + } + target := filepath.Join(output, name) + err = os.Remove(target) + if err != nil && !os.IsNotExist(err) { + return fmt.Errorf("Fail to remove target go file after sed: %w", err) + } + err = os.Rename(source, target) + if err != nil { + return fmt.Errorf("Fail to rename after sed: %w", err) + } } } + + return nil } -func buildServer(output string, protoPaths []string) { - args := []string{"build", "-o", output + "grpcserver", output + "server.go"} - for _, path := range protoPaths { - args = append(args, output+getProtoName(path)+".pb.go") +func buildServer(output string) error { + files, err := ioutil.ReadDir(output) + if err != nil { + log.Fatalf("Can't read dir for go files %s. %v\n", output, err) } + + args := []string{"build", "-o", binaryName} + for _, fi := range files { + name := fi.Name() + if fi.Mode().IsRegular() && strings.HasSuffix(name, ".go") { + args = append(args, name) + } + } + build := exec.Command("go", args...) + build.Dir = output + build.Env = []string{ + "GO111MODULE=off", + "GOPATH=" + goPath, + "HOME=" + os.Getenv("HOME"), + } build.Stdout = os.Stdout build.Stderr = os.Stderr - err := build.Run() - if err != nil { - log.Fatal(err) - } + err = build.Run() + return err } -func runGrpcServer(output string) (*exec.Cmd, <-chan error) { - run := exec.Command(output + "grpcserver") +func runGrpcServer(ctx context.Context, output string) (<-chan struct{}, error) { + run := exec.Command(output + binaryName) run.Stdout = os.Stdout run.Stderr = os.Stderr err := run.Start() if err != nil { - log.Fatal(err) + return nil, err } - fmt.Printf("grpc server pid: %d\n", run.Process.Pid) - runerr := make(chan error) + log.Printf("grpc server pid: %d\n", run.Process.Pid) + done := make(chan struct{}) + go func() { + err := run.Wait() + status := run.ProcessState.Sys().(syscall.WaitStatus) + signaled := status.Signaled() + if !signaled { + log.Fatal(err) + } + close(done) + }() go func() { - runerr <- run.Wait() + <-ctx.Done() + log.Printf("Stopping gRPC Server. pid: %d\n", run.Process.Pid) + run.Process.Kill() }() - return run, runerr + return done, nil +} + +type Options struct { + Port string + BindAddr string + Output string +} + +const defaultUploadPort = "4772" + +func uploadServer(ctx context.Context, opt Options, srvrs *servers) <-chan struct{} { + if opt.Port == "" { + opt.Port = defaultUploadPort + } + addr := opt.BindAddr + ":" + opt.Port + r := chi.NewRouter() + r.Post("/upload", func(w http.ResponseWriter, r *http.Request) { + body, err := ioutil.ReadAll(r.Body) + if err != nil { + log.Printf("Error Retrieving the File: %v\n", err) + failed(w, err) + return + } + + // shutdown + srvrs.shutdown() + + err = Unzip(body, opt.Output) + if err != nil { + failed(w, err) + return + } + + // boot + err = srvrs.boot(ctx) + if err != nil { + failed(w, err) + } + }) + + log.Println("Serving proto upload on http://" + addr) + srv := http.Server{ + Addr: addr, + Handler: r, + } + go func() { + if err := srv.ListenAndServe(); err != http.ErrServerClosed { + // Error starting or closing listener: + log.Fatalf("HTTP proto upload server ListenAndServe: %v", err) + } + }() + done := make(chan struct{}) + go func() { + <-ctx.Done() + log.Printf("HTTP proto upload server Shutdown") + if err := srv.Shutdown(ctx); err != nil && err != context.Canceled { + // Error from closing listeners, or context cancel: + log.Printf("Error: HTTP proto upload server Shutdown: %v", err) + } + close(done) + }() + return done +} + +func failed(w http.ResponseWriter, err error) { + w.WriteHeader(http.StatusInternalServerError) + w.Write([]byte(err.Error())) +} + +// Unzip will decompress a zip bytes, moving all files and folders +// within the zip bytes (parameter 1) to an output directory (parameter 2). +func Unzip(src []byte, dest string) error { + r, err := zip.NewReader(bytes.NewReader(src), int64(len(src))) + if err != nil { + log.Fatal(err) + } + + var filenames []string + + for _, f := range r.File { + + // Store filename/path for returning and using later on + fPath := filepath.Join(dest, f.Name) + + // Check for ZipSlip. More Info: http://bit.ly/2MsjAWE + if !strings.HasPrefix(fPath, filepath.Clean(dest)+string(os.PathSeparator)) { + return fmt.Errorf("%s: illegal file path", fPath) + } + + filenames = append(filenames, fPath) + + if f.FileInfo().IsDir() { + // Make Folder + os.MkdirAll(fPath, os.ModePerm) + continue + } + + // Make File + if err = os.MkdirAll(filepath.Dir(fPath), os.ModePerm); err != nil { + return err + } + + outFile, err := os.OpenFile(fPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode()) + if err != nil { + return err + } + + rc, err := f.Open() + if err != nil { + return err + } + + _, err = io.Copy(outFile, rc) + + // Close the file without defer to close before next iteration of loop + outFile.Close() + rc.Close() + + if err != nil { + return err + } + } + return nil +} + +func expandImports(names []string) []string { + dirs := []string{} + for _, name := range names { + files, err := ioutil.ReadDir(name) + if err != nil { + log.Fatalf("Can't read dir for imports from %s. %v\n", name, err) + } + + for _, fi := range files { + switch mode := fi.Mode(); { + case mode.IsDir(): + dirs = append(dirs, filepath.Join(name, fi.Name())) + } + } + } + return dirs +} + +func expandDirs(names []string) []string { + paths := []string{} + for _, name := range names { + fi, err := os.Stat(name) + if err != nil { + log.Println(err) + continue + } + switch mode := fi.Mode(); { + case mode.IsDir(): + err := filepath.Walk(name, + func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + if info.Mode().IsDir() || !strings.HasSuffix(info.Name(), ".proto") { + return nil + } + paths = append(paths, path) + return nil + }) + if err != nil { + log.Fatal(err) + } + case mode.IsRegular(): + log.Fatal("Only dir are allowed. Not a dir " + name) + } + } + return paths } diff --git a/protoc-gen-gripmock/generator.go b/protoc-gen-gripmock/generator.go index eb9cf7c9..4cb03eb7 100644 --- a/protoc-gen-gripmock/generator.go +++ b/protoc-gen-gripmock/generator.go @@ -67,6 +67,7 @@ type generatorParam struct { } type Service struct { + Package string Name string Methods []methodTemplate } @@ -109,8 +110,8 @@ func init() { } func generateServer(protos []*descriptor.FileDescriptorProto, opt *Options) error { - services := extractServices(protos) deps := resolveDependencies(protos) + services := extractServices(protos, deps) param := generatorParam{ Services: services, @@ -203,11 +204,12 @@ func getGoPackage(proto *descriptor.FileDescriptorProto) (alias string, goPackag } // change the structure also translate method type -func extractServices(protos []*descriptor.FileDescriptorProto) []Service { +func extractServices(protos []*descriptor.FileDescriptorProto, deps map[string]string) []Service { svcTmp := []Service{} for _, proto := range protos { for _, svc := range proto.GetService() { var s Service + s.Package = resolvePackage(deps, proto.GetPackage()) s.Name = svc.GetName() methods := make([]methodTemplate, len(svc.Method)) for j, method := range svc.Method { @@ -223,8 +225,8 @@ func extractServices(protos []*descriptor.FileDescriptorProto) []Service { methods[j] = methodTemplate{ Name: strings.Title(*method.Name), ServiceName: svc.GetName(), - Input: getMessageType(protos, proto.GetDependency(), method.GetInputType()), - Output: getMessageType(protos, proto.GetDependency(), method.GetOutputType()), + Input: formatMessageType(deps, method.GetInputType()), + Output: formatMessageType(deps, method.GetOutputType()), MethodType: tipe, } } @@ -235,26 +237,22 @@ func extractServices(protos []*descriptor.FileDescriptorProto) []Service { return svcTmp } -func getMessageType(protos []*descriptor.FileDescriptorProto, deps []string, tipe string) string { - split := strings.Split(tipe, ".")[1:] +func resolvePackage(deps map[string]string, pack string) string { + for _, alias := range deps { + if alias == pack { + return pack + } + } + return "" +} + +func formatMessageType(deps map[string]string, t string) string { + split := strings.Split(t, ".")[1:] targetPackage := strings.Join(split[:len(split)-1], ".") + targetPackage = resolvePackage(deps, targetPackage) targetType := split[len(split)-1] - for _, dep := range deps { - for _, proto := range protos { - if proto.GetName() != dep || proto.GetPackage() != targetPackage { - continue - } - - for _, msg := range proto.GetMessageType() { - if msg.GetName() == targetType { - alias, _ := getGoPackage(proto) - if alias != "" { - alias += "." - } - return fmt.Sprintf("%s%s", alias, msg.GetName()) - } - } - } + if targetPackage == "" { + return targetType } - return targetType + return targetPackage + "." + targetType } diff --git a/protoc-gen-gripmock/server.tmpl b/protoc-gen-gripmock/server.tmpl index c6e52dbf..b8d9d8a3 100644 --- a/protoc-gen-gripmock/server.tmpl +++ b/protoc-gen-gripmock/server.tmpl @@ -3,6 +3,7 @@ package main import ( "bytes" + "context" "encoding/json" "fmt" "io" @@ -11,7 +12,6 @@ import ( "net" "net/http" - "golang.org/x/net/context" "google.golang.org/grpc" "google.golang.org/grpc/reflection" ) @@ -39,7 +39,7 @@ func main() { {{ end }} reflection.Register(s) - fmt.Println("Serving gRPC on tcp://" + TCP_ADDRESS) + log.Println("Serving gRPC on tcp://" + TCP_ADDRESS) if err := s.Serve(lis); err != nil { log.Fatalf("failed to serve: %v", err) } @@ -129,7 +129,7 @@ func (s *{{.ServiceName}}) {{.Name}}(stream {{.ServiceName}}_{{.Name}}Server) er {{ define "register_services" }} - Register{{.Name}}Server(s, &{{.Name}}{}) + {{if .Package}}{{.Package}}.{{end}}Register{{.Name}}Server(s, &{{.Name}}{}) {{ end }} {{ define "find_stub" }} diff --git a/stub/stub.go b/stub/stub.go index 819b317f..56cdb007 100644 --- a/stub/stub.go +++ b/stub/stub.go @@ -1,13 +1,14 @@ package stub import ( + "context" "encoding/json" "fmt" "io/ioutil" "log" "net/http" "strings" - + "github.com/go-chi/chi" ) @@ -19,7 +20,7 @@ type Options struct { const DEFAULT_PORT = "4771" -func RunStubServer(opt Options) { +func RunStubServer(ctx context.Context, opt Options) <-chan struct{} { if opt.Port == "" { opt.Port = DEFAULT_PORT } @@ -34,11 +35,29 @@ func RunStubServer(opt Options) { readStubFromFile(opt.StubPath) } - fmt.Println("Serving stub admin on http://" + addr) + log.Println("Serving stub admin on http://" + addr) + srv := http.Server{ + Addr: addr, + Handler: r, + } + done := make(chan struct{}) + go func() { + if err := srv.ListenAndServe(); err != http.ErrServerClosed { + // Error starting or closing listener: + log.Fatalf("HTTP stub server ListenAndServe: %v", err) + } + close(done) + }() go func() { - err := http.ListenAndServe(addr, r) - log.Fatal(err) + <-ctx.Done() + log.Printf("HTTP stub server Shutdown") + if err := srv.Shutdown(ctx); err != nil && err != context.Canceled { + // Error from closing listeners, or context cancel: + log.Printf("Error: HTTP stub server Shutdown: %v", err) + } }() + + return done } func responseError(err error, w http.ResponseWriter) { @@ -106,7 +125,7 @@ func validateStub(stub *Stub) error { if stub.Method == "" { return fmt.Errorf("Method name can't be emtpy") } - + // due to golang implementation // method name must capital stub.Method = strings.Title(stub.Method) @@ -143,11 +162,11 @@ func handleFindStub(w http.ResponseWriter, r *http.Request) { responseError(err, w) return } - + // due to golang implementation // method name must capital stub.Method = strings.Title(stub.Method) - + output, err := findStub(stub) if err != nil { log.Println(err) diff --git a/tool/tool.go b/tool/tool.go new file mode 100644 index 00000000..b43bcdaa --- /dev/null +++ b/tool/tool.go @@ -0,0 +1,96 @@ +package tool + +import ( + "archive/zip" + "bytes" + "errors" + "io/ioutil" + "net/http" + "os" + "path/filepath" +) + +func UploadJsonFile(addr string, filePath string) (int, error) { + file, err := os.Open(filePath) + if err != nil { + return 0, err + } + b, err := ioutil.ReadAll(file) + if err != nil { + return 0, err + } + + return Upload(addr, "application/json", b) +} + +func ZipFolderAndUpload(addr string, folder string) (int, error) { + b, err := ZipFolder(folder) + if err != nil { + return 0, err + } + + return Upload(addr, "binary/octet-stream", b) +} + +func ZipFolder(folder string) ([]byte, error) { + // Create a buffer to write our archive to. + buf := new(bytes.Buffer) + + // Create a new zip archive. + w := zip.NewWriter(buf) + + err := filepath.Walk(folder, func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + if path == folder { + return nil + } + relPath := path[len(folder)+1:] + if info.IsDir() { + relPath += "/" + } + f, err := w.Create(relPath) + if err != nil { + return err + } + if info.IsDir() { + return nil + } + + b, err := ioutil.ReadFile(path) + if err != nil { + return err + } + _, err = f.Write([]byte(b)) + return err + }) + if err != nil { + return nil, err + } + + // Make sure to check the error on Close. + err = w.Close() + if err != nil { + return nil, err + } + return buf.Bytes(), nil +} + +func Upload(addr string, mimeType string, payload []byte) (int, error) { + res, err := http.Post(addr, mimeType, bytes.NewReader(payload)) + if err != nil { + return 0, err + } + defer res.Body.Close() + message, err := ioutil.ReadAll(res.Body) + if err != nil { + return 0, err + } + + if res.StatusCode != http.StatusOK { + return res.StatusCode, errors.New(string(message)) + } + + return res.StatusCode, nil +} From 0969416184717897eeaa33344b743e3c61079cab Mon Sep 17 00:00:00 2001 From: Paulo Pereira Date: Sat, 29 Aug 2020 20:45:54 +0100 Subject: [PATCH 2/8] revert to getMessageType --- protoc-gen-gripmock/generator.go | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/protoc-gen-gripmock/generator.go b/protoc-gen-gripmock/generator.go index 4cb03eb7..082fa8d7 100644 --- a/protoc-gen-gripmock/generator.go +++ b/protoc-gen-gripmock/generator.go @@ -225,8 +225,8 @@ func extractServices(protos []*descriptor.FileDescriptorProto, deps map[string]s methods[j] = methodTemplate{ Name: strings.Title(*method.Name), ServiceName: svc.GetName(), - Input: formatMessageType(deps, method.GetInputType()), - Output: formatMessageType(deps, method.GetOutputType()), + Input: getMessageType(protos, proto.GetDependency(), method.GetInputType()), + Output: getMessageType(protos, proto.GetDependency(), method.GetOutputType()), MethodType: tipe, } } @@ -246,13 +246,26 @@ func resolvePackage(deps map[string]string, pack string) string { return "" } -func formatMessageType(deps map[string]string, t string) string { - split := strings.Split(t, ".")[1:] +func getMessageType(protos []*descriptor.FileDescriptorProto, deps []string, tipe string) string { + split := strings.Split(tipe, ".")[1:] targetPackage := strings.Join(split[:len(split)-1], ".") - targetPackage = resolvePackage(deps, targetPackage) targetType := split[len(split)-1] - if targetPackage == "" { - return targetType + for _, dep := range deps { + for _, proto := range protos { + if proto.GetName() != dep || proto.GetPackage() != targetPackage { + continue + } + + for _, msg := range proto.GetMessageType() { + if msg.GetName() == targetType { + alias, _ := getGoPackage(proto) + if alias != "" { + alias += "." + } + return fmt.Sprintf("%s%s", alias, msg.GetName()) + } + } + } } - return targetPackage + "." + targetType + return targetType } From e1f8197c0f6fbd41ab43d4eaeb63da030324ef42 Mon Sep 17 00:00:00 2001 From: Paulo Pereira Date: Sat, 29 Aug 2020 20:56:54 +0100 Subject: [PATCH 3/8] increase test timeout to 10s --- example/well_known_types/client/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/example/well_known_types/client/main.go b/example/well_known_types/client/main.go index f26fec55..25280c2c 100644 --- a/example/well_known_types/client/main.go +++ b/example/well_known_types/client/main.go @@ -11,7 +11,7 @@ import ( ) func main() { - ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) + ctx, cancel := context.WithTimeout(context.Background(), time.Second*10) defer cancel() // Set up a connection to the server. From 1a753e49e5d9221e9503c015666b6ab42b35f69c Mon Sep 17 00:00:00 2001 From: Paulo Pereira Date: Sat, 29 Aug 2020 21:08:40 +0100 Subject: [PATCH 4/8] increase test timeout to 20s --- example/well_known_types/client/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/example/well_known_types/client/main.go b/example/well_known_types/client/main.go index 25280c2c..68e7e3f5 100644 --- a/example/well_known_types/client/main.go +++ b/example/well_known_types/client/main.go @@ -11,7 +11,7 @@ import ( ) func main() { - ctx, cancel := context.WithTimeout(context.Background(), time.Second*10) + ctx, cancel := context.WithTimeout(context.Background(), time.Second*20) defer cancel() // Set up a connection to the server. From 0b30ca8de3a93f31cb962ae579268d16376e4fae Mon Sep 17 00:00:00 2001 From: Paulo Pereira Date: Sun, 30 Aug 2020 00:48:51 +0100 Subject: [PATCH 5/8] fix failing test --- Dockerfile | 7 +------ example/well_known_types/client/main.go | 2 +- go.mod | 2 +- go.sum | 5 ++--- protoc-gen-gripmock/generator.go | 5 ++--- 5 files changed, 7 insertions(+), 14 deletions(-) diff --git a/Dockerfile b/Dockerfile index e53be36b..2c390246 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,5 @@ FROM golang:alpine -ENV GO111MODULE=off - RUN mkdir /proto RUN mkdir /stubs @@ -11,12 +9,9 @@ RUN apk -U --no-cache add git protobuf RUN go get -u -v github.com/golang/protobuf/protoc-gen-go \ google.golang.org/grpc \ google.golang.org/grpc/reflection \ - golang.org/x/net/context \ github.com/go-chi/chi \ github.com/lithammer/fuzzysearch/fuzzy \ - golang.org/x/tools/imports \ - google.golang.org/protobuf/reflect/protoreflect \ - google.golang.org/protobuf/runtime/protoimpl + golang.org/x/tools/imports RUN go get -u -v github.com/gobuffalo/packr/v2/... \ github.com/gobuffalo/packr/v2/packr2 diff --git a/example/well_known_types/client/main.go b/example/well_known_types/client/main.go index 68e7e3f5..f26fec55 100644 --- a/example/well_known_types/client/main.go +++ b/example/well_known_types/client/main.go @@ -11,7 +11,7 @@ import ( ) func main() { - ctx, cancel := context.WithTimeout(context.Background(), time.Second*20) + ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() // Set up a connection to the server. diff --git a/go.mod b/go.mod index 58f3aee9..43211dc7 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/stretchr/testify v1.6.1 golang.org/x/net v0.0.0-20200226121028-0de0cce0169b golang.org/x/tools v0.0.0-20200308013534-11ec41452d41 - google.golang.org/genproto v0.0.0-20200825200019-8632dd797987 + google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 google.golang.org/grpc v1.31.1 google.golang.org/protobuf v1.25.0 ) diff --git a/go.sum b/go.sum index f0869b75..dc4ce77e 100644 --- a/go.sum +++ b/go.sum @@ -152,6 +152,7 @@ golang.org/x/crypto v0.0.0-20191122220453-ac88ee75c92c/go.mod h1:LzIPMQfyMNhhGPh 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 h1:XQyxROzUlZH+WIQwySDgnISgOivlhjIEwaQaJEJrrN0= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/mod v0.2.0 h1:KU7oHjnv3XNWfa5COkzUifxZmxp1TyI7ImMXqFxLwvQ= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= @@ -203,9 +204,8 @@ google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9Ywl google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 h1:+kGHl1aib/qcwaRi1CbqBZ1rk19r85MNUf8HaBghugY= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20200825200019-8632dd797987 h1:PDIOdWxZ8eRizhKa1AAvY53xsvLB1cWorMjslvY3VA8= -google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= @@ -221,7 +221,6 @@ google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzi google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= diff --git a/protoc-gen-gripmock/generator.go b/protoc-gen-gripmock/generator.go index 082fa8d7..8a18647a 100644 --- a/protoc-gen-gripmock/generator.go +++ b/protoc-gen-gripmock/generator.go @@ -196,9 +196,8 @@ func getGoPackage(proto *descriptor.FileDescriptorProto) (alias string, goPackag goPackage = splits[0] alias = splits[1] } else { - splitSlash := strings.Split(proto.GetName(), "/") - split := strings.Split(splitSlash[len(splitSlash)-1], ".") - alias = split[0] + splitSlash := strings.Split(goPackage, "/") + alias = splitSlash[len(splitSlash)-1] } return } From c6fc82a4f7f9dcfbfb28c7e36410c70a145aa575 Mon Sep 17 00:00:00 2001 From: Paulo Pereira Date: Sun, 30 Aug 2020 23:22:01 +0100 Subject: [PATCH 6/8] minimize breaking changes --- Readme.md | 29 +++++++++--- example/multi-project/entrypoint.sh | 2 +- example/simple/entrypoint.sh | 2 +- gripmock.go | 68 ++++++++++++++++++++++------- 4 files changed, 77 insertions(+), 24 deletions(-) diff --git a/Readme.md b/Readme.md index c3bad716..57483ee7 100644 --- a/Readme.md +++ b/Readme.md @@ -148,18 +148,19 @@ The reason I created a fork was because I did a lot of changes and ended up drif ## New features This fork adds the following features: -* Packages at the same Level - proto files defined at their own folders and none of them is at the min package (top level) -* Immediate sub directories can represent different imports * Uploading proto files +* When uploading protos, immediate sub directories can represent different imports, if tthe flag `--isd` is set +* Packages at the same Level - proto files defined at their own folders and none of them is at the min package (top level) Changes: * modules * generating proto into GOPATH +* Argument list now considers directories. These directories are added to the import list and all the proto files inside are also added to the list of compiled protos. Breaking changes: -* It no longer accepts individual proto files, but rather directories with the proto files * `GOPATH` needs to be defined + ### Packages at the same Level Now we can address the scenario where we have proto files packages where none is at the main package. Imagine that we have two projects. One is `foo` and the other is `bar`. `foo` has a dependency on `bar`. @@ -181,10 +182,26 @@ Unfortunately just copying doesn't work for all projects, since some projects ha I am thinking about when some projects define the packages in the `protoc` command like `--go_out=Mbar/bar.proto=this/is/a/package:.`. What we have to do is the opposite. Change the proto files used for the mocks so that all have the option `go_package` and that the imports reflect the current structure. -If sub dirs import flag is set `-isb`, all immediate sub dirs from the list of dirs passed as argument, like `foo` and `bar` inside `/proto`, -will be imported by the `protoc` with the compile option `-I` allowing us to have different packages in different sub directories. +If sub dirs import flag is set `-isb`, all immediate sub dirs from the uploaded protos. +Consider a zip file with the following tree dir. -Make sure that: +``` +proto +├── prj-bar +│   └── bar +│   ├── bar.pb.go +│   └── bar.proto +└── prj-foo + └── foo + ├── foo.pb.go + ├── foo.proto + ├── hello.pb.go + └── hello.prot +``` + +`prj-foo` and `prj-bar` inside `/proto`, will be imported by the `protoc` with the compile option `-I` allowing us to have different packages in different sub directories. + +Please make you proto well behaved: * all proto files have the `option go_package`. * eg: `option go_package = "github.com/quintans/foo";` * all the imports must be relative to the current file structure diff --git a/example/multi-project/entrypoint.sh b/example/multi-project/entrypoint.sh index c65dd434..a7cfd762 100755 --- a/example/multi-project/entrypoint.sh +++ b/example/multi-project/entrypoint.sh @@ -2,6 +2,6 @@ # this file is used by .github/workflows/integration-test.yml -gripmock --stub=example/multi-project/stub -isd example/multi-project/proto & +gripmock --stub=example/multi-project/stub --imports=example/multi-project/proto/prj-bar,example/multi-project/proto/prj-foo example/multi-project/proto & go run example/multi-project/client/*.go diff --git a/example/simple/entrypoint.sh b/example/simple/entrypoint.sh index bd006981..6299cc49 100755 --- a/example/simple/entrypoint.sh +++ b/example/simple/entrypoint.sh @@ -2,6 +2,6 @@ # this file is used by .github/workflows/integration-test.yml -gripmock --stub=example/simple/stub example/simple & +gripmock --stub=example/simple/stub example/simple/simple.proto & go run example/simple/client/*.go diff --git a/gripmock.go b/gripmock.go index 383f3c5b..cbc03fd5 100644 --- a/gripmock.go +++ b/gripmock.go @@ -41,8 +41,8 @@ func main() { stubPath := flag.String("stub", "", "Path where the stub files are (Optional)") upPort := flag.String("up-port", "4772", "Port of upload proto server") upBindAddr := flag.String("up-listen", "", "Address the upload proto server will bind to. Default to localhost, set to 0.0.0.0 to use from another machine") - imports := flag.String("imports", "/protobuf", "comma separated imports path. default path /protobuf is where gripmock Dockerfile install WKT protos") - importSubdirs := flag.Bool("isd", false, "Immediate sub dirs from the list of dirs passed as argument, will be imported") + imports := flag.String("imports", "", "comma separated imports path. Path /protobuf is always set. It is where gripmock Dockerfile install WKT protos") + importSubdirs := flag.Bool("isd", false, "Immediate sub dirs of the upload, will be imported") // for backwards compatibility if len(os.Args) > 1 && os.Args[1] == "gripmock" { os.Args = append(os.Args[:1], os.Args[2:]...) @@ -69,8 +69,6 @@ func main() { os.Mkdir(*output, os.ModePerm) } - // parse proto files - protoPaths := flag.Args() err := os.RemoveAll(upProtoFolder) if err != nil { log.Fatalf("did not remove %s: %v", upProtoFolder, err) @@ -79,11 +77,46 @@ func main() { if err != nil { log.Fatalf("did not create %s: %v", upProtoFolder, err) } - protoPaths = append(protoPaths, upProtoFolder) - importDirs := strings.Split(*imports, ",") - if len(importDirs) == 0 { - log.Fatal("No importable dirs") + importDirs := []string{"/protobuf"} + impSplit := strings.Split(*imports, ",") + if len(*imports) > 0 { + importDirs = append(importDirs, impSplit...) + } + + // parse proto files + args := flag.Args() + protoPaths, imps := expandDirs(args) + + // if the first arg is a file, add the its dir to the list of imports + if len(args) > 0 { + name := args[0] + fi, err := os.Stat(name) + if err != nil { + log.Fatalf("Unable to get the status of %s: %v", name, err) + return + } + if fi.Mode().IsRegular() { + protodirs := strings.Split(name, "/") + protodir := "" + if len(protodirs) > 0 { + protodir = strings.Join(protodirs[:len(protodirs)-1], "/") + } + imps = append(imps, protodir) + } + } + + // only add argument folders if they are not inside imports list + for _, i := range imps { + found := false + for _, is := range impSplit { + if strings.Contains(is, i) { + found = true + } + } + if !found { + importDirs = append(importDirs, i) + } } ctx, cancel := context.WithCancel(context.Background()) @@ -136,7 +169,8 @@ type servers struct { } func (s *servers) boot(ctx context.Context) error { - protoPaths := expandDirs(s.params.protoPaths) + protos, _ := expandDirs([]string{upProtoFolder}) + protoPaths := append(s.params.protoPaths, protos...) if len(protoPaths) == 0 { log.Println("No proto files found. Skipping stub and grpc boot.") return nil @@ -160,9 +194,9 @@ func (s *servers) boot(ctx context.Context) error { var importDirs []string if s.params.importSubdirs { - importDirs = append(s.params.imports, expandImports(s.params.protoPaths)...) + importDirs = append(s.params.imports, importSudDirs([]string{upProtoFolder})...) } else { - importDirs = append(s.params.imports, s.params.protoPaths...) + importDirs = append(s.params.imports, upProtoFolder) } pp := protocParam{ @@ -442,7 +476,7 @@ func Unzip(src []byte, dest string) error { return nil } -func expandImports(names []string) []string { +func importSudDirs(names []string) []string { dirs := []string{} for _, name := range names { files, err := ioutil.ReadDir(name) @@ -460,8 +494,9 @@ func expandImports(names []string) []string { return dirs } -func expandDirs(names []string) []string { +func expandDirs(names []string) ([]string, []string) { paths := []string{} + imps := []string{} for _, name := range names { fi, err := os.Stat(name) if err != nil { @@ -470,6 +505,7 @@ func expandDirs(names []string) []string { } switch mode := fi.Mode(); { case mode.IsDir(): + imps = append(imps, name) err := filepath.Walk(name, func(path string, info os.FileInfo, err error) error { if err != nil { @@ -484,9 +520,9 @@ func expandDirs(names []string) []string { if err != nil { log.Fatal(err) } - case mode.IsRegular(): - log.Fatal("Only dir are allowed. Not a dir " + name) + case mode.IsRegular() && strings.HasSuffix(name, ".proto"): + paths = append(paths, name) } } - return paths + return paths, imps } From 677bb164d1c0c7af321fdbceb22a7c48131454eb Mon Sep 17 00:00:00 2001 From: Paulo Pereira Date: Mon, 31 Aug 2020 13:05:59 +0100 Subject: [PATCH 7/8] move servers to its own package and added upload reset --- example/upload/client/main.go | 9 +- go.mod | 1 + go.sum | 7 + gripmock.go | 435 ++-------------------------------- servers/mocks/servers.go | 101 ++++++++ servers/servers.go | 305 ++++++++++++++++++++++++ tool/tool.go | 9 + upload/ip/bar/bar.proto | 9 + upload/ip/foo/foo.proto | 10 + upload/ip/foo/hello.proto | 12 + upload/upload.go | 184 ++++++++++++++ upload/upload_test.go | 89 +++++++ 12 files changed, 761 insertions(+), 410 deletions(-) create mode 100644 servers/mocks/servers.go create mode 100644 servers/servers.go create mode 100644 upload/ip/bar/bar.proto create mode 100644 upload/ip/foo/foo.proto create mode 100644 upload/ip/foo/hello.proto create mode 100644 upload/upload.go create mode 100644 upload/upload_test.go diff --git a/example/upload/client/main.go b/example/upload/client/main.go index c1a114df..6073cfbc 100644 --- a/example/upload/client/main.go +++ b/example/upload/client/main.go @@ -8,6 +8,7 @@ import ( "github.com/tokopedia/gripmock/example/upload/client/bar" "github.com/tokopedia/gripmock/example/upload/client/foo" + "github.com/tokopedia/gripmock/servers" "github.com/tokopedia/gripmock/tool" "google.golang.org/grpc" ) @@ -16,8 +17,14 @@ func main() { ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() + _, err := tool.UploadAsJson("http://localhost:4772/reset", servers.Reset{ + ImportSubDirs: false, + }) + if err != nil { + log.Fatalf("did not reset upload server: %v", err) + } // upload proto files - _, err := tool.ZipFolderAndUpload("http://localhost:4772/upload", "example/upload/proto") + _, err = tool.ZipFolderAndUpload("http://localhost:4772/upload", "example/upload/proto") if err != nil { log.Fatalf("did not upload proto: %v", err) } diff --git a/go.mod b/go.mod index 43211dc7..60046a0a 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,7 @@ go 1.15 require ( github.com/go-chi/chi v4.1.2+incompatible github.com/gobuffalo/packr/v2 v2.8.0 + github.com/golang/mock v1.4.3 github.com/golang/protobuf v1.4.2 github.com/lithammer/fuzzysearch v1.1.0 github.com/stretchr/testify v1.6.1 diff --git a/go.sum b/go.sum index dc4ce77e..4d935287 100644 --- a/go.sum +++ b/go.sum @@ -43,7 +43,10 @@ github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7a github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.1.1 h1:G5FRp8JnTd7RQH5kemVNlMeyXQAztQ3mOWV95KxsXH8= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.4.3 h1:GV+pQPG/EUUbkh47niozDcADz6go/dUwhVzdUQHIVRw= +github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= 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= @@ -182,6 +185,7 @@ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894 h1:Cz4ceDQGXuKRnVBDTS23GTn/pU5OE2C0WrNTOYK1Uuc= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +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.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= @@ -192,6 +196,7 @@ golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGm 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-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200308013534-11ec41452d41 h1:9Di9iYgOt9ThCipBxChBVhgNipDoE5mxO84rQV7D0FE= @@ -236,3 +241,5 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +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/gripmock.go b/gripmock.go index cbc03fd5..4050982d 100644 --- a/gripmock.go +++ b/gripmock.go @@ -1,34 +1,22 @@ package main import ( - "archive/zip" - "bytes" "context" "flag" "fmt" - "io" - "io/ioutil" "log" - "net/http" "os" - "os/exec" "os/signal" - "path/filepath" "strings" "syscall" - "github.com/go-chi/chi" + "github.com/tokopedia/gripmock/servers" "github.com/tokopedia/gripmock/stub" + "github.com/tokopedia/gripmock/upload" ) const ( - version = "v0.0.1" - binaryName = "grpcserver" -) - -var ( - upProtoFolder = "/temp/up-proto" - goPath = "" + version = "v0.0.1" ) func main() { @@ -55,29 +43,20 @@ func main() { flag.Parse() log.Println("Starting GripMock", version) - goPath = os.Getenv("GOPATH") + goPath := os.Getenv("GOPATH") if goPath == "" { log.Fatal("GOPATH is empty") } if *output == "" { *output = os.Getenv("GOPATH") + "/src/grpc" } - upProtoFolder = goPath + upProtoFolder + upProtoFolder := goPath + "/temp/up-proto" // for safety *output += "/" if _, err := os.Stat(*output); os.IsNotExist(err) { os.Mkdir(*output, os.ModePerm) } - err := os.RemoveAll(upProtoFolder) - if err != nil { - log.Fatalf("did not remove %s: %v", upProtoFolder, err) - } - err = os.MkdirAll(upProtoFolder, os.ModePerm) - if err != nil { - log.Fatalf("did not create %s: %v", upProtoFolder, err) - } - importDirs := []string{"/protobuf"} impSplit := strings.Split(*imports, ",") if len(*imports) > 0 { @@ -86,7 +65,7 @@ func main() { // parse proto files args := flag.Args() - protoPaths, imps := expandDirs(args) + protoPaths, imps := servers.ExpandDirs(args) // if the first arg is a file, add the its dir to the list of imports if len(args) > 0 { @@ -120,32 +99,38 @@ func main() { } ctx, cancel := context.WithCancel(context.Background()) - srvrs := &servers{ - options: stub.Options{ + srvrs := servers.New( + goPath, + stub.Options{ StubPath: *stubPath, Port: *adminport, BindAddr: *adminBindAddr, }, - params: protocParam{ - protoPaths: protoPaths, - adminPort: *adminport, - grpcAddress: *grpcBindAddr, - grpcPort: *grpcPort, - imports: importDirs, - importSubdirs: *importSubdirs, + servers.ProtocParam{ + ProtoPaths: protoPaths, + AdminPort: *adminport, + GrpcAddress: *grpcBindAddr, + GrpcPort: *grpcPort, + Imports: importDirs, + ImportSubdirs: *importSubdirs, }, - grpcOutput: *output, + *output, + upProtoFolder, + ) + + err := srvrs.CleanUploadDir() + if err != nil { + log.Fatalf("Unable to clean upload dir: %v", err) } - err = srvrs.boot(ctx) + err = srvrs.Boot(ctx) if err != nil { log.Fatal(err) } - upDone := uploadServer(ctx, Options{ + upDone := upload.RunUploadServer(ctx, upload.Options{ BindAddr: *upBindAddr, Port: *upPort, - Output: upProtoFolder, }, srvrs) var term = make(chan os.Signal) @@ -154,375 +139,7 @@ func main() { case <-term: log.Println("Signaled to shutdown") cancel() - srvrs.shutdown() + srvrs.Shutdown() <-upDone } } - -type servers struct { - options stub.Options - params protocParam - grpcOutput string - cancel context.CancelFunc - stubDone <-chan struct{} - grpcDone <-chan struct{} -} - -func (s *servers) boot(ctx context.Context) error { - protos, _ := expandDirs([]string{upProtoFolder}) - protoPaths := append(s.params.protoPaths, protos...) - if len(protoPaths) == 0 { - log.Println("No proto files found. Skipping stub and grpc boot.") - return nil - } - - ctx, cancel := context.WithCancel(ctx) - s.cancel = cancel - - // clean output folder - err := os.RemoveAll(s.grpcOutput) - if err != nil { - return err - } - err = os.Mkdir(s.grpcOutput, os.ModePerm) - if err != nil { - return err - } - - // run admin stub server - s.stubDone = stub.RunStubServer(ctx, s.options) - - var importDirs []string - if s.params.importSubdirs { - importDirs = append(s.params.imports, importSudDirs([]string{upProtoFolder})...) - } else { - importDirs = append(s.params.imports, upProtoFolder) - } - - pp := protocParam{ - imports: importDirs, - adminPort: s.params.adminPort, - grpcAddress: s.params.grpcAddress, - grpcPort: s.params.grpcPort, - protoPaths: protoPaths, - } - - // generate pb.go and grpc server based on proto - err = generateProtoc(pp, s.grpcOutput) - if err != nil { - return err - } - - // build the server - err = buildServer(s.grpcOutput) - if err != nil { - return err - } - - // and run - s.grpcDone, err = runGrpcServer(ctx, s.grpcOutput) - return err -} - -func (s *servers) shutdown() { - if s.cancel != nil { - s.cancel() - <-s.stubDone - <-s.grpcDone - } -} - -func getProtoName(path string) string { - paths := strings.Split(path, "/") - filename := paths[len(paths)-1] - return strings.Split(filename, ".")[0] -} - -type protocParam struct { - protoPaths []string - adminPort string - grpcAddress string - grpcPort string - imports []string - importSubdirs bool -} - -func generateProtoc(param protocParam, output string) error { - src := goPath + "/src" - args := []string{} - // include well-known-types - for _, i := range param.imports { - args = append(args, "-I", i) - log.Println("Importing", i) - } - args = append(args, "--go_out=plugins=grpc:"+src) - args = append(args, fmt.Sprintf("--gripmock_out=admin-port=%s,grpc-address=%s,grpc-port=%s:%s", - param.adminPort, param.grpcAddress, param.grpcPort, output)) - args = append(args, param.protoPaths...) - protoc := exec.Command("protoc", args...) - protoc.Stdout = os.Stdout - protoc.Stderr = os.Stderr - err := protoc.Run() - if err != nil { - return fmt.Errorf("Fail on protoc: %w", err) - } - - // change package to "main" on top level generated code - files, err := ioutil.ReadDir(src) - if err != nil { - log.Fatalf("Can't read dir for imports from %s. %v\n", output, err) - } - - for _, fi := range files { - name := fi.Name() - if fi.Mode().IsRegular() && strings.HasSuffix(name, ".pb.go") { - source := filepath.Join(src, name) - sed := exec.Command("sed", "-i", `s/^package \w*$/package main/`, source) - sed.Stderr = os.Stderr - sed.Stdout = os.Stdout - err = sed.Run() - if err != nil { - return fmt.Errorf("Fail on sed: %w", err) - } - target := filepath.Join(output, name) - err = os.Remove(target) - if err != nil && !os.IsNotExist(err) { - return fmt.Errorf("Fail to remove target go file after sed: %w", err) - } - err = os.Rename(source, target) - if err != nil { - return fmt.Errorf("Fail to rename after sed: %w", err) - } - } - } - - return nil -} - -func buildServer(output string) error { - files, err := ioutil.ReadDir(output) - if err != nil { - log.Fatalf("Can't read dir for go files %s. %v\n", output, err) - } - - args := []string{"build", "-o", binaryName} - for _, fi := range files { - name := fi.Name() - if fi.Mode().IsRegular() && strings.HasSuffix(name, ".go") { - args = append(args, name) - } - } - - build := exec.Command("go", args...) - build.Dir = output - build.Env = []string{ - "GO111MODULE=off", - "GOPATH=" + goPath, - "HOME=" + os.Getenv("HOME"), - } - build.Stdout = os.Stdout - build.Stderr = os.Stderr - err = build.Run() - return err -} - -func runGrpcServer(ctx context.Context, output string) (<-chan struct{}, error) { - run := exec.Command(output + binaryName) - run.Stdout = os.Stdout - run.Stderr = os.Stderr - err := run.Start() - if err != nil { - return nil, err - } - log.Printf("grpc server pid: %d\n", run.Process.Pid) - done := make(chan struct{}) - go func() { - err := run.Wait() - status := run.ProcessState.Sys().(syscall.WaitStatus) - signaled := status.Signaled() - if !signaled { - log.Fatal(err) - } - close(done) - }() - go func() { - <-ctx.Done() - log.Printf("Stopping gRPC Server. pid: %d\n", run.Process.Pid) - run.Process.Kill() - }() - return done, nil -} - -type Options struct { - Port string - BindAddr string - Output string -} - -const defaultUploadPort = "4772" - -func uploadServer(ctx context.Context, opt Options, srvrs *servers) <-chan struct{} { - if opt.Port == "" { - opt.Port = defaultUploadPort - } - addr := opt.BindAddr + ":" + opt.Port - r := chi.NewRouter() - r.Post("/upload", func(w http.ResponseWriter, r *http.Request) { - body, err := ioutil.ReadAll(r.Body) - if err != nil { - log.Printf("Error Retrieving the File: %v\n", err) - failed(w, err) - return - } - - // shutdown - srvrs.shutdown() - - err = Unzip(body, opt.Output) - if err != nil { - failed(w, err) - return - } - - // boot - err = srvrs.boot(ctx) - if err != nil { - failed(w, err) - } - }) - - log.Println("Serving proto upload on http://" + addr) - srv := http.Server{ - Addr: addr, - Handler: r, - } - go func() { - if err := srv.ListenAndServe(); err != http.ErrServerClosed { - // Error starting or closing listener: - log.Fatalf("HTTP proto upload server ListenAndServe: %v", err) - } - }() - done := make(chan struct{}) - go func() { - <-ctx.Done() - log.Printf("HTTP proto upload server Shutdown") - if err := srv.Shutdown(ctx); err != nil && err != context.Canceled { - // Error from closing listeners, or context cancel: - log.Printf("Error: HTTP proto upload server Shutdown: %v", err) - } - close(done) - }() - return done -} - -func failed(w http.ResponseWriter, err error) { - w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte(err.Error())) -} - -// Unzip will decompress a zip bytes, moving all files and folders -// within the zip bytes (parameter 1) to an output directory (parameter 2). -func Unzip(src []byte, dest string) error { - r, err := zip.NewReader(bytes.NewReader(src), int64(len(src))) - if err != nil { - log.Fatal(err) - } - - var filenames []string - - for _, f := range r.File { - - // Store filename/path for returning and using later on - fPath := filepath.Join(dest, f.Name) - - // Check for ZipSlip. More Info: http://bit.ly/2MsjAWE - if !strings.HasPrefix(fPath, filepath.Clean(dest)+string(os.PathSeparator)) { - return fmt.Errorf("%s: illegal file path", fPath) - } - - filenames = append(filenames, fPath) - - if f.FileInfo().IsDir() { - // Make Folder - os.MkdirAll(fPath, os.ModePerm) - continue - } - - // Make File - if err = os.MkdirAll(filepath.Dir(fPath), os.ModePerm); err != nil { - return err - } - - outFile, err := os.OpenFile(fPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode()) - if err != nil { - return err - } - - rc, err := f.Open() - if err != nil { - return err - } - - _, err = io.Copy(outFile, rc) - - // Close the file without defer to close before next iteration of loop - outFile.Close() - rc.Close() - - if err != nil { - return err - } - } - return nil -} - -func importSudDirs(names []string) []string { - dirs := []string{} - for _, name := range names { - files, err := ioutil.ReadDir(name) - if err != nil { - log.Fatalf("Can't read dir for imports from %s. %v\n", name, err) - } - - for _, fi := range files { - switch mode := fi.Mode(); { - case mode.IsDir(): - dirs = append(dirs, filepath.Join(name, fi.Name())) - } - } - } - return dirs -} - -func expandDirs(names []string) ([]string, []string) { - paths := []string{} - imps := []string{} - for _, name := range names { - fi, err := os.Stat(name) - if err != nil { - log.Println(err) - continue - } - switch mode := fi.Mode(); { - case mode.IsDir(): - imps = append(imps, name) - err := filepath.Walk(name, - func(path string, info os.FileInfo, err error) error { - if err != nil { - return err - } - if info.Mode().IsDir() || !strings.HasSuffix(info.Name(), ".proto") { - return nil - } - paths = append(paths, path) - return nil - }) - if err != nil { - log.Fatal(err) - } - case mode.IsRegular() && strings.HasSuffix(name, ".proto"): - paths = append(paths, name) - } - } - return paths, imps -} diff --git a/servers/mocks/servers.go b/servers/mocks/servers.go new file mode 100644 index 00000000..92724988 --- /dev/null +++ b/servers/mocks/servers.go @@ -0,0 +1,101 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: servers.go + +// Package mocks is a generated GoMock package. +package mocks + +import ( + context "context" + gomock "github.com/golang/mock/gomock" + servers "github.com/tokopedia/gripmock/servers" + reflect "reflect" +) + +// MockRebooter is a mock of Rebooter interface +type MockRebooter struct { + ctrl *gomock.Controller + recorder *MockRebooterMockRecorder +} + +// MockRebooterMockRecorder is the mock recorder for MockRebooter +type MockRebooterMockRecorder struct { + mock *MockRebooter +} + +// NewMockRebooter creates a new mock instance +func NewMockRebooter(ctrl *gomock.Controller) *MockRebooter { + mock := &MockRebooter{ctrl: ctrl} + mock.recorder = &MockRebooterMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use +func (m *MockRebooter) EXPECT() *MockRebooterMockRecorder { + return m.recorder +} + +// Boot mocks base method +func (m *MockRebooter) Boot(ctx context.Context) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Boot", ctx) + ret0, _ := ret[0].(error) + return ret0 +} + +// Boot indicates an expected call of Boot +func (mr *MockRebooterMockRecorder) Boot(ctx interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Boot", reflect.TypeOf((*MockRebooter)(nil).Boot), ctx) +} + +// Shutdown mocks base method +func (m *MockRebooter) Shutdown() { + m.ctrl.T.Helper() + m.ctrl.Call(m, "Shutdown") +} + +// Shutdown indicates an expected call of Shutdown +func (mr *MockRebooterMockRecorder) Shutdown() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Shutdown", reflect.TypeOf((*MockRebooter)(nil).Shutdown)) +} + +// Reset mocks base method +func (m *MockRebooter) Reset(reset servers.Reset) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "Reset", reset) +} + +// Reset indicates an expected call of Reset +func (mr *MockRebooterMockRecorder) Reset(reset interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Reset", reflect.TypeOf((*MockRebooter)(nil).Reset), reset) +} + +// UploadDir mocks base method +func (m *MockRebooter) UploadDir() string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "UploadDir") + ret0, _ := ret[0].(string) + return ret0 +} + +// UploadDir indicates an expected call of UploadDir +func (mr *MockRebooterMockRecorder) UploadDir() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UploadDir", reflect.TypeOf((*MockRebooter)(nil).UploadDir)) +} + +// CleanUploadDir mocks base method +func (m *MockRebooter) CleanUploadDir() error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CleanUploadDir") + ret0, _ := ret[0].(error) + return ret0 +} + +// CleanUploadDir indicates an expected call of CleanUploadDir +func (mr *MockRebooterMockRecorder) CleanUploadDir() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CleanUploadDir", reflect.TypeOf((*MockRebooter)(nil).CleanUploadDir)) +} diff --git a/servers/servers.go b/servers/servers.go new file mode 100644 index 00000000..9d2cfe9d --- /dev/null +++ b/servers/servers.go @@ -0,0 +1,305 @@ +package servers + +import ( + "context" + "fmt" + "io/ioutil" + "log" + "os" + "os/exec" + "path/filepath" + "strings" + "syscall" + + "github.com/tokopedia/gripmock/stub" +) + +//go:generate mockgen -source=servers.go -destination mocks/servers.go -package=mocks Rebooter + +const ( + binaryName = "grpcserver" +) + +type Rebooter interface { + Boot(ctx context.Context) error + Shutdown() + Reset(reset Reset) + UploadDir() string + CleanUploadDir() error +} + +type Servers struct { + goPath string + options stub.Options + params ProtocParam + grpcOutput string + uploadDir string + cancel context.CancelFunc + stubDone <-chan struct{} + grpcDone <-chan struct{} +} + +type ProtocParam struct { + ProtoPaths []string + AdminPort string + GrpcAddress string + GrpcPort string + Imports []string + ImportSubdirs bool +} + +type Reset struct { + ImportSubDirs bool `json:"isd"` +} + +func New(goPath string, options stub.Options, params ProtocParam, grpcOutput string, uploadDir string) *Servers { + return &Servers{ + goPath: goPath, + options: options, + params: params, + grpcOutput: grpcOutput, + uploadDir: uploadDir, + } +} + +func (s *Servers) UploadDir() string { + return s.uploadDir +} + +func (s *Servers) Boot(ctx context.Context) error { + protos, _ := ExpandDirs([]string{s.uploadDir}) + protoPaths := append(s.params.ProtoPaths, protos...) + if len(protoPaths) == 0 { + log.Println("No proto files found. Skipping stub and grpc boot.") + return nil + } + + ctx, cancel := context.WithCancel(ctx) + s.cancel = cancel + + // clean output folder + err := os.RemoveAll(s.grpcOutput) + if err != nil { + return err + } + err = os.Mkdir(s.grpcOutput, os.ModePerm) + if err != nil { + return err + } + + // run admin stub server + s.stubDone = stub.RunStubServer(ctx, s.options) + + var importDirs []string + if s.params.ImportSubdirs { + importDirs = append(s.params.Imports, ImportSudDirs([]string{s.uploadDir})...) + } else { + importDirs = append(s.params.Imports, s.uploadDir) + } + + pp := ProtocParam{ + Imports: importDirs, + AdminPort: s.params.AdminPort, + GrpcAddress: s.params.GrpcAddress, + GrpcPort: s.params.GrpcPort, + ProtoPaths: protoPaths, + } + + // generate pb.go and grpc server based on proto + err = generateProtoc(s.goPath, pp, s.grpcOutput) + if err != nil { + return err + } + + // build the server + err = buildServer(s.goPath, s.grpcOutput) + if err != nil { + return err + } + + // and run + s.grpcDone, err = runGrpcServer(ctx, s.grpcOutput) + return err +} + +func (s *Servers) CleanUploadDir() error { + // clear output upload folder + err := os.RemoveAll(s.uploadDir) + if err != nil { + return err + } + err = os.MkdirAll(s.uploadDir, os.ModePerm) + if err != nil { + return err + } + return nil +} + +func (s *Servers) Shutdown() { + if s.cancel != nil { + s.cancel() + <-s.stubDone + <-s.grpcDone + } +} + +func (s *Servers) Reset(reset Reset) { + s.params.ImportSubdirs = reset.ImportSubDirs +} + +func generateProtoc(goPath string, other ProtocParam, output string) error { + src := goPath + "/src" + args := []string{} + // include well-known-types + for _, i := range other.Imports { + args = append(args, "-I", i) + log.Println("Importing", i) + } + args = append(args, "--go_out=plugins=grpc:"+src) + args = append(args, fmt.Sprintf("--gripmock_out=admin-port=%s,grpc-address=%s,grpc-port=%s:%s", + other.AdminPort, other.GrpcAddress, other.GrpcPort, output)) + args = append(args, other.ProtoPaths...) + protoc := exec.Command("protoc", args...) + protoc.Stdout = os.Stdout + protoc.Stderr = os.Stderr + err := protoc.Run() + if err != nil { + return fmt.Errorf("Fail on protoc: %w", err) + } + + // change package to "main" on top level generated code + files, err := ioutil.ReadDir(src) + if err != nil { + log.Fatalf("Can't read dir for imports from %s. %v\n", output, err) + } + + for _, fi := range files { + name := fi.Name() + if fi.Mode().IsRegular() && strings.HasSuffix(name, ".pb.go") { + source := filepath.Join(src, name) + sed := exec.Command("sed", "-i", `s/^package \w*$/package main/`, source) + sed.Stderr = os.Stderr + sed.Stdout = os.Stdout + err = sed.Run() + if err != nil { + return fmt.Errorf("Fail on sed: %w", err) + } + target := filepath.Join(output, name) + err = os.Remove(target) + if err != nil && !os.IsNotExist(err) { + return fmt.Errorf("Fail to remove target go file after sed: %w", err) + } + err = os.Rename(source, target) + if err != nil { + return fmt.Errorf("Fail to rename after sed: %w", err) + } + } + } + + return nil +} + +func buildServer(goPath string, output string) error { + files, err := ioutil.ReadDir(output) + if err != nil { + log.Fatalf("Can't read dir for go files %s. %v\n", output, err) + } + + args := []string{"build", "-o", binaryName} + for _, fi := range files { + name := fi.Name() + if fi.Mode().IsRegular() && strings.HasSuffix(name, ".go") { + args = append(args, name) + } + } + + build := exec.Command("go", args...) + build.Dir = output + build.Env = []string{ + "GO111MODULE=off", + "GOPATH=" + goPath, + "HOME=" + os.Getenv("HOME"), + } + build.Stdout = os.Stdout + build.Stderr = os.Stderr + err = build.Run() + return err +} + +func runGrpcServer(ctx context.Context, output string) (<-chan struct{}, error) { + run := exec.Command(output + binaryName) + run.Stdout = os.Stdout + run.Stderr = os.Stderr + err := run.Start() + if err != nil { + return nil, err + } + log.Printf("grpc server pid: %d\n", run.Process.Pid) + done := make(chan struct{}) + go func() { + err := run.Wait() + status := run.ProcessState.Sys().(syscall.WaitStatus) + signaled := status.Signaled() + if !signaled { + log.Fatal(err) + } + close(done) + }() + go func() { + <-ctx.Done() + log.Printf("Stopping gRPC Server. pid: %d\n", run.Process.Pid) + run.Process.Kill() + }() + return done, nil +} + +func ImportSudDirs(names []string) []string { + dirs := []string{} + for _, name := range names { + files, err := ioutil.ReadDir(name) + if err != nil { + log.Fatalf("Can't read dir for imports from %s. %v\n", name, err) + } + + for _, fi := range files { + switch mode := fi.Mode(); { + case mode.IsDir(): + dirs = append(dirs, filepath.Join(name, fi.Name())) + } + } + } + return dirs +} + +func ExpandDirs(names []string) ([]string, []string) { + paths := []string{} + imps := []string{} + for _, name := range names { + fi, err := os.Stat(name) + if err != nil { + log.Println(err) + continue + } + switch mode := fi.Mode(); { + case mode.IsDir(): + imps = append(imps, name) + err := filepath.Walk(name, + func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + if info.Mode().IsDir() || !strings.HasSuffix(info.Name(), ".proto") { + return nil + } + paths = append(paths, path) + return nil + }) + if err != nil { + log.Fatal(err) + } + case mode.IsRegular() && strings.HasSuffix(name, ".proto"): + paths = append(paths, name) + } + } + return paths, imps +} diff --git a/tool/tool.go b/tool/tool.go index b43bcdaa..9b8e00e4 100644 --- a/tool/tool.go +++ b/tool/tool.go @@ -3,6 +3,7 @@ package tool import ( "archive/zip" "bytes" + "encoding/json" "errors" "io/ioutil" "net/http" @@ -94,3 +95,11 @@ func Upload(addr string, mimeType string, payload []byte) (int, error) { return res.StatusCode, nil } + +func UploadAsJson(addr string, payload interface{}) (int, error) { + data, err := json.Marshal(payload) + if err != nil { + return 0, err + } + return Upload(addr, "application/json", data) +} diff --git a/upload/ip/bar/bar.proto b/upload/ip/bar/bar.proto new file mode 100644 index 00000000..adc6ec5a --- /dev/null +++ b/upload/ip/bar/bar.proto @@ -0,0 +1,9 @@ +syntax = "proto3"; + +option go_package="github.com/tokopedia/gripmock/bar"; + +package bar; + +message Request{ + string name = 1; +} diff --git a/upload/ip/foo/foo.proto b/upload/ip/foo/foo.proto new file mode 100644 index 00000000..d88f0657 --- /dev/null +++ b/upload/ip/foo/foo.proto @@ -0,0 +1,10 @@ +syntax = "proto3"; + +option go_package="github.com/tokopedia/gripmock/foo"; + +package foo; + +message Response { + string message = 1; + int32 return_code = 2; +} diff --git a/upload/ip/foo/hello.proto b/upload/ip/foo/hello.proto new file mode 100644 index 00000000..72383cd3 --- /dev/null +++ b/upload/ip/foo/hello.proto @@ -0,0 +1,12 @@ +syntax = "proto3"; + +option go_package="github.com/tokopedia/gripmock/foo"; + +package foo; + +import "bar/bar.proto"; +import "foo/foo.proto"; + +service Greeter { + rpc Greet (bar.Request) returns (Response); +} diff --git a/upload/upload.go b/upload/upload.go new file mode 100644 index 00000000..5b795fb8 --- /dev/null +++ b/upload/upload.go @@ -0,0 +1,184 @@ +package upload + +import ( + "archive/zip" + "bytes" + "context" + "encoding/json" + "fmt" + "io" + "io/ioutil" + "log" + "net/http" + "os" + "path/filepath" + "strings" + + "github.com/go-chi/chi" + "github.com/tokopedia/gripmock/servers" +) + +type Options struct { + Port string + BindAddr string +} + +const defaultUploadPort = "4772" + +func RunUploadServer(ctx context.Context, opt Options, rebooter servers.Rebooter) <-chan struct{} { + us := uploadServer{ + rebooter: rebooter, + ctx: ctx, + } + if opt.Port == "" { + opt.Port = defaultUploadPort + } + addr := opt.BindAddr + ":" + opt.Port + r := chi.NewRouter() + r.Post("/upload", us.handleUpload) + r.Post("/reset", us.handleReset) + + log.Println("Serving proto upload on http://" + addr) + srv := http.Server{ + Addr: addr, + Handler: r, + } + go func() { + if err := srv.ListenAndServe(); err != http.ErrServerClosed { + // Error starting or closing listener: + log.Fatalf("HTTP proto upload server ListenAndServe: %v", err) + } + }() + done := make(chan struct{}) + go func() { + <-ctx.Done() + log.Printf("HTTP proto upload server Shutdown") + if err := srv.Shutdown(ctx); err != nil && err != context.Canceled { + // Error from closing listeners, or context cancel: + log.Printf("Error: HTTP proto upload server Shutdown: %v", err) + } + close(done) + }() + return done +} + +func responseError(w http.ResponseWriter, err error) { + w.WriteHeader(http.StatusInternalServerError) + w.Write([]byte(err.Error())) +} + +// Unzip will decompress a zip bytes, moving all files and folders +// within the zip bytes (parameter 1) to an output directory (parameter 2). +func Unzip(src []byte, dest string) error { + r, err := zip.NewReader(bytes.NewReader(src), int64(len(src))) + if err != nil { + return err + } + + var filenames []string + + for _, f := range r.File { + + // Store filename/path for returning and using later on + fPath := filepath.Join(dest, f.Name) + + // Check for ZipSlip. More Info: http://bit.ly/2MsjAWE + if !strings.HasPrefix(fPath, filepath.Clean(dest)+string(os.PathSeparator)) { + return fmt.Errorf("%s: illegal file path", fPath) + } + + filenames = append(filenames, fPath) + + if f.FileInfo().IsDir() { + // Make Folder + os.MkdirAll(fPath, os.ModePerm) + continue + } + + // Make File + if err = os.MkdirAll(filepath.Dir(fPath), os.ModePerm); err != nil { + return err + } + + outFile, err := os.OpenFile(fPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode()) + if err != nil { + return err + } + + rc, err := f.Open() + if err != nil { + return err + } + + _, err = io.Copy(outFile, rc) + + // Close the file without defer to close before next iteration of loop + outFile.Close() + rc.Close() + + if err != nil { + return err + } + } + return nil +} + +type uploadServer struct { + rebooter servers.Rebooter + ctx context.Context +} + +func (s uploadServer) handleUpload(w http.ResponseWriter, r *http.Request) { + body, err := ioutil.ReadAll(r.Body) + if err != nil { + log.Printf("Error Retrieving the File: %v\n", err) + responseError(w, err) + return + } + + // shutdown + s.rebooter.Shutdown() + + err = Unzip(body, s.rebooter.UploadDir()) + if err != nil { + responseError(w, err) + return + } + + // boot + err = s.rebooter.Boot(s.ctx) + if err != nil { + responseError(w, err) + } +} + +func (s uploadServer) handleReset(w http.ResponseWriter, r *http.Request) { + body, err := ioutil.ReadAll(r.Body) + if err != nil { + responseError(w, err) + return + } + + reset := servers.Reset{} + err = json.Unmarshal(body, &reset) + if err != nil { + responseError(w, err) + return + } + + // shutdown + s.rebooter.Shutdown() + + err = s.rebooter.CleanUploadDir() + if err != nil { + log.Fatalf("Unable to clean upload dir: %v", err) + } + + s.rebooter.Reset(reset) + + // boot + err = s.rebooter.Boot(s.ctx) + if err != nil { + responseError(w, err) + } +} diff --git a/upload/upload_test.go b/upload/upload_test.go new file mode 100644 index 00000000..63a2d128 --- /dev/null +++ b/upload/upload_test.go @@ -0,0 +1,89 @@ +package upload + +import ( + "bytes" + "io/ioutil" + "net/http" + "net/http/httptest" + "testing" + + "github.com/golang/mock/gomock" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/tokopedia/gripmock/servers" + "github.com/tokopedia/gripmock/servers/mocks" + "github.com/tokopedia/gripmock/tool" +) + +func TestUploader(t *testing.T) { + type test struct { + name string + rebooterMock func(t *testing.T) (*mocks.MockRebooter, *gomock.Controller) + mock func(t *testing.T) *http.Request + handler func(us uploadServer, w http.ResponseWriter, r *http.Request) + expect string + } + + cases := []test{ + { + name: "upload ok", + rebooterMock: func(t *testing.T) (*mocks.MockRebooter, *gomock.Controller) { + ctrl := gomock.NewController(t) + rebooter := mocks.NewMockRebooter(ctrl) + rebooter.EXPECT().UploadDir().Return("ip").Times(1) + rebooter.EXPECT().Shutdown().Times(1) + rebooter.EXPECT().Boot(gomock.Any()).Times(1) + return rebooter, ctrl + }, + mock: func(t *testing.T) *http.Request { + payload, err := tool.ZipFolder("../example/upload/proto") + require.NoError(t, err) + return httptest.NewRequest("POST", "/upload", bytes.NewReader([]byte(payload))) + }, + handler: func(us uploadServer, w http.ResponseWriter, r *http.Request) { + us.handleUpload(w, r) + }, + expect: "", + }, + { + name: "reset import dirs true", + rebooterMock: func(t *testing.T) (*mocks.MockRebooter, *gomock.Controller) { + ctrl := gomock.NewController(t) + rebooter := mocks.NewMockRebooter(ctrl) + rebooter.EXPECT().Shutdown().Times(1) + rebooter.EXPECT().CleanUploadDir().Times(1) + rebooter.EXPECT().Reset(servers.Reset{ + ImportSubDirs: true, + }).Times(1) + rebooter.EXPECT().Boot(gomock.Any()).Times(1) + return rebooter, ctrl + }, + mock: func(t *testing.T) *http.Request { + payload := `{"isd": true}` + return httptest.NewRequest("POST", "/reset", bytes.NewReader([]byte(payload))) + }, + handler: func(us uploadServer, w http.ResponseWriter, r *http.Request) { + us.handleReset(w, r) + }, + expect: "", + }, + } + + for _, v := range cases { + t.Run(v.name, func(t *testing.T) { + rebooter, ctrl := v.rebooterMock(t) + defer ctrl.Finish() + us := uploadServer{ + rebooter: rebooter, + } + + wrt := httptest.NewRecorder() + req := v.mock(t) + v.handler(us, wrt, req) + res, err := ioutil.ReadAll(wrt.Result().Body) + + assert.NoError(t, err) + assert.Equal(t, v.expect, string(res)) + }) + } +} From ca59913264e4f4bbebcf5495ce29ead0d3f7236f Mon Sep 17 00:00:00 2001 From: Paulo Pereira Date: Tue, 1 Sep 2020 12:22:16 +0100 Subject: [PATCH 8/8] unused proto files --- upload/ip/bar/bar.proto | 9 --------- upload/ip/foo/foo.proto | 10 ---------- upload/ip/foo/hello.proto | 12 ------------ 3 files changed, 31 deletions(-) delete mode 100644 upload/ip/bar/bar.proto delete mode 100644 upload/ip/foo/foo.proto delete mode 100644 upload/ip/foo/hello.proto diff --git a/upload/ip/bar/bar.proto b/upload/ip/bar/bar.proto deleted file mode 100644 index adc6ec5a..00000000 --- a/upload/ip/bar/bar.proto +++ /dev/null @@ -1,9 +0,0 @@ -syntax = "proto3"; - -option go_package="github.com/tokopedia/gripmock/bar"; - -package bar; - -message Request{ - string name = 1; -} diff --git a/upload/ip/foo/foo.proto b/upload/ip/foo/foo.proto deleted file mode 100644 index d88f0657..00000000 --- a/upload/ip/foo/foo.proto +++ /dev/null @@ -1,10 +0,0 @@ -syntax = "proto3"; - -option go_package="github.com/tokopedia/gripmock/foo"; - -package foo; - -message Response { - string message = 1; - int32 return_code = 2; -} diff --git a/upload/ip/foo/hello.proto b/upload/ip/foo/hello.proto deleted file mode 100644 index 72383cd3..00000000 --- a/upload/ip/foo/hello.proto +++ /dev/null @@ -1,12 +0,0 @@ -syntax = "proto3"; - -option go_package="github.com/tokopedia/gripmock/foo"; - -package foo; - -import "bar/bar.proto"; -import "foo/foo.proto"; - -service Greeter { - rpc Greet (bar.Request) returns (Response); -}