diff --git a/examples/code_generator/Makefile b/examples/code_generator/Makefile new file mode 100644 index 00000000..53955bff --- /dev/null +++ b/examples/code_generator/Makefile @@ -0,0 +1,16 @@ + +proto_path="${HOME}/code/" + +.PHONY: pb plugin gen +pb: + protoc -I=. -I=${proto_path} --gogofaster_out=plugins=grpc:. proto/*.proto + +code_gen: cmd/code_gen.go cmd/go.mod + cd cmd && make build && cd .. && mv cmd/code_gen ./ + #go mod tidy && go build -o code_gen cmd/code_gen.go + +plugin: + ./code_gen -proto_path=${proto_path} -source_proto=./proto/my_test_grpc_plugin.proto -target_path=./ + +gen: pb code_gen plugin + @echo ok diff --git a/examples/code_generator/README.md b/examples/code_generator/README.md new file mode 100644 index 00000000..cd5465fd --- /dev/null +++ b/examples/code_generator/README.md @@ -0,0 +1,45 @@ +This is a command tool to generate proto3 to grpc plugin golang source code. + +# How to use +## 0.install protoc +```shell +brew install protobuf +``` + +## 1.download gogo proto: +```shell +mkdir -p ${HOME}/code/github.com/gogo/ +cd ${HOME}/code/github.com/gogo/ +git clone https://github.com/gogo/protobuf.git +``` +And install protoc plugin of gogo proto: +```shell +go install github.com/gogo/protobuf/protoc-gen-gogo +``` + +## 2.add a proto3 idl file: +see example: [./proto/my_test_grpc_plugin.proto] +* define Request and Response message format +* define a service + - use extension to add a plugin name: + - `option (plugin_name) = "my_plugin_1";` + - It will use service name when plugin name not set +* use protoc to generate XX.pb.go files +```shell +make pb proto_path="${HOME}/code/" +``` + +## 3.generate code +```shell +make gen proto_path="${HOME}/code/" +``` + +see current directory `github.com/...`. + +# Test plugin +```shell +cd examples/code_generator/github.com/hashicorp/go-plugin/examples/my_test_grpc_plugin_callee +go mod tidy -compat=1.17 && go build +# add code to callee_test.go +go test +``` diff --git a/examples/code_generator/cmd/Makefile b/examples/code_generator/cmd/Makefile new file mode 100644 index 00000000..8cb400ce --- /dev/null +++ b/examples/code_generator/cmd/Makefile @@ -0,0 +1,6 @@ + +build: + go build -o code_gen code_gen.go + +test: + ./code_gen -proto_path=${HOME}/code/ -source_proto=../proto/my_test_grpc_plugin.proto -target_path=./ diff --git a/examples/code_generator/cmd/code_gen.go b/examples/code_generator/cmd/code_gen.go new file mode 100644 index 00000000..d67b357a --- /dev/null +++ b/examples/code_generator/cmd/code_gen.go @@ -0,0 +1,271 @@ +package main + +import ( + _ "embed" + "flag" + "fmt" + "log" + "os" + "path/filepath" + "regexp" + "strings" + "text/template" + + "github.com/jhump/protoreflect/desc" + "github.com/jhump/protoreflect/desc/protoparse" + "google.golang.org/protobuf/types/descriptorpb" +) + +var ( + targetPath = flag.String("target_path", "./", "-target_path=/path/to/save/go/files") + protoPath = flag.String("proto_path", "", "-proto_path=/path/to/include/proto/files") + sourceProto = flag.String("source_proto", "", "-source_proto=/path/to/one/source/proto/file") +) + +func checkArgs() { + if s, err := os.Stat(*targetPath); os.IsNotExist(err) || !s.IsDir() { + log.Fatalln("target_path not exists(or not a dir): " + *targetPath) + } + if s, err := os.Stat(*protoPath); os.IsNotExist(err) || !s.IsDir() { + log.Fatalln("proto_path not exists(or not a dir): " + *protoPath) + } + if s, err := os.Stat(*sourceProto); os.IsNotExist(err) || s.IsDir() { + log.Fatalln("source_proto not exists(or not a file): " + *sourceProto) + } +} + +func parseFile(protoPath *string, sourceProto *string) *desc.FileDescriptor { + p := protoparse.Parser{ + IncludeSourceCodeInfo: true, + ImportPaths: []string{*protoPath, "."}, + } + fds, err := p.ParseFiles(*sourceProto) + if err != nil { + log.Fatalf("load file [%s] fail, err=%+v", *sourceProto, err) + } + if len(fds) != 1 { + log.Fatalf("load file [%s] fail, count=%d", *sourceProto, len(fds)) + } + return fds[0] +} + +//go:embed template_files/xx.plugin.go.tpl +var templateOfService string + +//go:embed template_files/go.mod.tpl +var templateOfGoMod string + +//go:embed template_files/callee/go.mod.tpl +var templateOfCalleeGoMod string + +//go:embed template_files/callee/callee.go.tpl +var templateOfCalleeDotGo string + +//go:embed template_files/callee/plugin/plugin.go.tpl +var templateOfCalleePluginGo string + +//go:embed template_files/callee/callee_test.go.tpl +var templateOfCalleeTestGo string + +var tService = template.Must(template.New("service").Parse(templateOfService)) +var tGoMod = template.Must(template.New("go.mod").Parse(templateOfGoMod)) +var tCalleeGoMod = template.Must(template.New("callee_go.mod").Parse(templateOfCalleeGoMod)) +var tCalleeDotGo = template.Must(template.New("callee.go").Parse(templateOfCalleeDotGo)) +var tCalleePluginGo = template.Must(template.New("callee_plugin.go").Parse(templateOfCalleePluginGo)) +var tCalleeTestGo = template.Must(template.New("callee_test.go").Parse(templateOfCalleeTestGo)) + +func createFile(p string) *os.File { + out, err := os.OpenFile(p, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, os.ModePerm) + if err != nil { + log.Fatalf("create file [%s] fail, err=%s", p, err.Error()) + } + return out +} + +func getMethodOfService(s *desc.ServiceDescriptor) []Method { + out := make([]Method, 0, len(s.GetMethods())) + for _, m := range s.GetMethods() { + out = append(out, Method{ + Name: makeFirstLetterUpperCase(m.GetName()), + InputType: m.GetInputType().GetName(), + OutputType: m.GetOutputType().GetName(), + }) + } + return out +} + +func makeFirstLetterUpperCase(s string) string { + return strings.ToUpper(s[:1]) + s[1:] +} + +var pluginNameRE = regexp.MustCompile(`\d+:["]?([^"]+)["]?`) + +func parsePluginName(s string) string { + arr := pluginNameRE.FindAllStringSubmatch(s, -1) + if len(arr) == 0 { + return "" + } + if len(arr[0]) < 2 { + return "" + } + return arr[0][1] +} + +func getServiceOptionOfPluginName(service *desc.ServiceDescriptor) string { + opt, ok := service.GetOptions().(*descriptorpb.ServiceOptions) + if !ok { + return service.GetName() + } + pluginName := parsePluginName(opt.String()) + if len(pluginName) > 0 { + return pluginName + } + // prefix := strconv.Itoa(int(E_PluginName.Field)) + ":" + // if strings.HasPrefix(opt.String(), prefix) { + // s := opt.String()[len(prefix):] + // if s[0] == '"' { + // s = s[1:] + // } + // if s[len(s)-1] == '"' { + // s = s[:len(s)-1] + // } + // return s + // } + return service.GetName() +} + +func getServiceInfo(pbFile *desc.FileDescriptor) []Service { + out := make([]Service, 0, len(pbFile.GetServices())) + for _, service := range pbFile.GetServices() { + out = append(out, Service{ + Name: service.GetName(), + PluginName: getServiceOptionOfPluginName(service), + Methods: getMethodOfService(service), + }) + } + return out +} + +type Method struct { + Name string + InputType string + OutputType string +} + +type Service struct { + Name string + PluginName string + Methods []Method +} + +type pbStruct struct { + Package string + FullPackagePath string + Services []Service +} + +// var E_PluginName = &proto.ExtensionDesc{ +// ExtendedType: (*descriptorpb.ServiceOptions)(nil), +// ExtensionType: (*string)(nil), +// Field: 51235, +// Name: "grpc_plugin.plugin_name", +// Tag: "bytes,51235,opt,name=plugin_name", +// Filename: "proto/my_test_grpc_plugin.proto", +// } + +func genPluginFile(fullPath string, pbFile *desc.FileDescriptor, st *pbStruct) { + packageName := filepath.Base(fullPath) + mainFile := createFile(filepath.Join(fullPath, fmt.Sprintf("%s.plugin.go", strings.ToLower(packageName)))) + defer mainFile.Close() + err := tService.Execute(mainFile, st) + if err != nil { + log.Fatalf("write xx.plugin.go fail:%+v", err) + } + // go.mod + goModFile := createFile(filepath.Join(fullPath, "go.mod")) + defer goModFile.Close() + err = tGoMod.Execute(goModFile, map[string]string{ + "FullPackagePath": *pbFile.GetFileOptions().GoPackage, + }) + if err != nil { + log.Fatalf("write go.mod fail:%+v", err) + } +} + +func mkdirs(d string) { + if _, err := os.Stat(d); os.IsNotExist(err) { + if err = os.MkdirAll(d, os.ModePerm); err != nil { + log.Fatalf("create dir [%s] fail, err=%s", d, err.Error()) + } + } +} + +func genCalleeFile(fullPath string, pbFile *desc.FileDescriptor, st *pbStruct) { + calleePath := fullPath + "_callee" + mkdirs(calleePath) + // go.mod + { + goModFile := createFile(filepath.Join(calleePath, "go.mod")) + defer goModFile.Close() + err := tCalleeGoMod.Execute(goModFile, st) + if err != nil { + log.Fatalf("write callee go.mod fail:%+v", err) + } + } + // callee.go + { + calleeGoFile := createFile(filepath.Join(calleePath, "callee.go")) + defer calleeGoFile.Close() + err := tCalleeDotGo.Execute(calleeGoFile, st) + if err != nil { + log.Fatalf("write callee/callee.go fail:%+v", err) + } + } + // callee_test.go + { + calleeTestFile := createFile(filepath.Join(calleePath, "callee_test.go")) + defer calleeTestFile.Close() + err := tCalleeTestGo.Execute(calleeTestFile, st) + if err != nil { + log.Fatalf("write callee/callee_test.go fail:%+v", err) + } + } + // plugin/ + pluginPath := calleePath + "/plugin" + mkdirs(pluginPath) + { + calleePluginGoFile := createFile(filepath.Join(pluginPath, "plugin.go")) + defer calleePluginGoFile.Close() + err := tCalleePluginGo.Execute(calleePluginGoFile, st) + if err != nil { + log.Fatalf("write callee/plugin/plugin.go fail:%+v", err) + } + } + +} + +func main() { + log.SetFlags(log.LstdFlags | log.Lshortfile) + flag.Parse() + checkArgs() + pbFile := parseFile(protoPath, sourceProto) + // log.Println(*pbFile.GetFileOptions().GoPackage) + fullPath := filepath.Join(*targetPath, *pbFile.GetFileOptions().GoPackage) + if _, err := os.Stat(fullPath); os.IsNotExist(err) { + log.Println("mkdir:", fullPath) + if err = os.MkdirAll(fullPath, os.ModePerm); err != nil { + log.Fatalln("mkdir fail: err=", err.Error()) + } + } + // goFilePath := filepath.Dir(fullPath) + // log.Println(goFilePath) + log.Println(fullPath) + packageName := filepath.Base(fullPath) + st := &pbStruct{ + Package: packageName, + FullPackagePath: *pbFile.GetFileOptions().GoPackage, + Services: getServiceInfo(pbFile), + } + genPluginFile(fullPath, pbFile, st) + genCalleeFile(fullPath, pbFile, st) +} diff --git a/examples/code_generator/cmd/go.mod b/examples/code_generator/cmd/go.mod new file mode 100644 index 00000000..2e525ae8 --- /dev/null +++ b/examples/code_generator/cmd/go.mod @@ -0,0 +1,14 @@ +module github.com/hashicorp/go-plugin/examples/code_generator + +go 1.17 + +require ( + github.com/gogo/protobuf v1.3.2 + github.com/jhump/protoreflect v1.14.0 + google.golang.org/protobuf v1.26.0 +) + +require ( + github.com/golang/protobuf v1.5.0 // indirect + google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 // indirect +) diff --git a/examples/code_generator/cmd/go.sum b/examples/code_generator/cmd/go.sum new file mode 100644 index 00000000..df1d2619 --- /dev/null +++ b/examples/code_generator/cmd/go.sum @@ -0,0 +1,128 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0 h1:LUVKkCeviFUMKqHa4tXIIij/lbhnMbP7Fn5wKdKkRh4= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/jhump/gopoet v0.0.0-20190322174617-17282ff210b3/go.mod h1:me9yfT6IJSlOL3FCfrg+L6yzUEZ+5jW6WHt4Sk+UPUI= +github.com/jhump/gopoet v0.1.0/go.mod h1:me9yfT6IJSlOL3FCfrg+L6yzUEZ+5jW6WHt4Sk+UPUI= +github.com/jhump/goprotoc v0.5.0/go.mod h1:VrbvcYrQOrTi3i0Vf+m+oqQWk9l72mjkJCYo7UvLHRQ= +github.com/jhump/protoreflect v1.11.0/go.mod h1:U7aMIjN0NWq9swDP7xDdoMfRHb35uiuTd3Z9nFXJf5E= +github.com/jhump/protoreflect v1.14.0 h1:MBbQK392K3u8NTLbKOCIi3XdI+y+c6yt5oMq0X3xviw= +github.com/jhump/protoreflect v1.14.0/go.mod h1:JytZfP5d0r8pVNLZvai7U/MCuTWITgrI4tTg7puQFKI= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +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-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.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-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-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201021035429-f5854403a974 h1:IX6qOQeG5uLjB/hjjwjedwfjND0hgjPMMyO1RoIXQNI= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f h1:+Nyd8tzPX9R7BWHguqsrbFdRx3WQ/1ib8I44HXV5yTA= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/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 h1:+kGHl1aib/qcwaRi1CbqBZ1rk19r85MNUf8HaBghugY= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.38.0 h1:/9BgsAsa5nWe26HqOlvlgJnqBuktYOLCgjCPqsa56W0= +google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/examples/code_generator/cmd/template_files/callee/callee.go.tpl b/examples/code_generator/cmd/template_files/callee/callee.go.tpl new file mode 100644 index 00000000..89c7ac27 --- /dev/null +++ b/examples/code_generator/cmd/template_files/callee/callee.go.tpl @@ -0,0 +1,29 @@ +package main + +import ( + "runtime" + + "github.com/hashicorp/go-plugin" + + pb "{{.FullPackagePath}}" + + imp "{{.FullPackagePath}}_callee/plugin" +) + +func StartPluginCallee() { + plugin.Serve(&plugin.ServeConfig{ + HandshakeConfig: pb.Handshake, + Plugins: map[string]plugin.Plugin{ +{{- range $item := .Services }} + "{{.PluginName}}": &pb.GRPC{{.Name}}{Impl: &imp.{{.Name}}Service{}}, +{{- end }} + }, + // A non-nil value here enables gRPC serving for this plugin... + GRPCServer: plugin.DefaultGRPCServer, + }) +} + +func main() { + runtime.GOMAXPROCS(1) + StartPluginCallee() +} diff --git a/examples/code_generator/cmd/template_files/callee/callee_test.go.tpl b/examples/code_generator/cmd/template_files/callee/callee_test.go.tpl new file mode 100644 index 00000000..b1267db4 --- /dev/null +++ b/examples/code_generator/cmd/template_files/callee/callee_test.go.tpl @@ -0,0 +1,59 @@ +package main + +import ( + "context" + "os/exec" + "testing" + + "github.com/hashicorp/go-plugin" + + pb "{{.FullPackagePath}}" +) + +func Test_Callee(t *testing.T) { + path := "./{{.Package}}_callee" // todo: `go build` first + pluginClientConfig := &plugin.ClientConfig{ + HandshakeConfig: pb.Handshake, + Cmd: exec.Command(path), + Plugins: map[string]plugin.Plugin{ + +{{- range $item := .Services }} + "{{.PluginName}}": &pb.GRPC{{.Name}}{}, +{{- end }} + }, + AllowedProtocols: []plugin.Protocol{plugin.ProtocolGRPC}, + } + client := plugin.NewClient(pluginClientConfig) + pluginClientConfig.Reattach = client.ReattachConfig() + protocol, err := client.Client() + if err != nil { + t.Errorf("new client error, err=%+v", err) + return + } +{{- range $item := .Services }} + { + raw, err := protocol.Dispense("{{.PluginName}}") + if err != nil { + t.Errorf("PluginName {{.PluginName}} error, err=%+v", err) + return + } + inst, ok := raw.(pb.{{.Name}}Server) + if !ok { + t.Errorf("interface type error") + return + } + // test each method +{{$CurService := .Name}} +{{- range $item := .Methods }} + { + rsp, err := inst.{{.Name}}(context.Background(), &pb.{{.InputType}}{/*todo: add param here*/}) + if err != nil { + t.Errorf("run error, err=%+v", err) + return + } + t.Logf("rsp=%+v", rsp) + } +{{- end }} + } +{{- end }} +} diff --git a/examples/code_generator/cmd/template_files/callee/go.mod.tpl b/examples/code_generator/cmd/template_files/callee/go.mod.tpl new file mode 100644 index 00000000..77c9d54d --- /dev/null +++ b/examples/code_generator/cmd/template_files/callee/go.mod.tpl @@ -0,0 +1,28 @@ +module {{.FullPackagePath}}_callee + +go 1.17 + +replace {{.FullPackagePath}} => ../{{.Package}} + +require ( + github.com/hashicorp/go-plugin v1.4.8 + {{.FullPackagePath}} v0.0.0-00010101000000-000000000000 +) + +require ( + github.com/fatih/color v1.7.0 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/protobuf v1.5.2 // indirect + github.com/hashicorp/go-hclog v0.14.1 // indirect + github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb // indirect + github.com/mattn/go-colorable v0.1.4 // indirect + github.com/mattn/go-isatty v0.0.10 // indirect + github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77 // indirect + github.com/oklog/run v1.0.0 // indirect + golang.org/x/net v0.0.0-20220722155237-a158d28d115b // indirect + golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f // indirect + golang.org/x/text v0.4.0 // indirect + google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 // indirect + google.golang.org/grpc v1.51.0 // indirect + google.golang.org/protobuf v1.28.1 // indirect +) diff --git a/examples/code_generator/cmd/template_files/callee/plugin/plugin.go.tpl b/examples/code_generator/cmd/template_files/callee/plugin/plugin.go.tpl new file mode 100644 index 00000000..588fe9ec --- /dev/null +++ b/examples/code_generator/cmd/template_files/callee/plugin/plugin.go.tpl @@ -0,0 +1,19 @@ +package plugin + +import ( + pb "{{.FullPackagePath}}" +) + +{{- range $item := .Services }} + +type {{.Name}}Service struct{} + +{{$CurService := .Name}} +{{- range $item := .Methods }} +// {{.Name}} Implement the interface of grpc +func (p *{{$CurService}}Service) {{.Name}}(req *pb.{{.InputType}}) *pb.{{.OutputType}} { + // todo: add logic here + return &pb.Response{} +} +{{- end }} +{{- end }} diff --git a/examples/code_generator/cmd/template_files/go.mod.tpl b/examples/code_generator/cmd/template_files/go.mod.tpl new file mode 100644 index 00000000..c2942a4f --- /dev/null +++ b/examples/code_generator/cmd/template_files/go.mod.tpl @@ -0,0 +1,25 @@ +module {{.FullPackagePath}} + +go 1.17 + +require ( + github.com/gogo/protobuf v1.3.2 + github.com/hashicorp/go-plugin v1.4.8 + google.golang.org/grpc v1.51.0 + google.golang.org/protobuf v1.28.1 +) + +require ( + github.com/fatih/color v1.7.0 // indirect + github.com/golang/protobuf v1.5.2 // indirect + github.com/hashicorp/go-hclog v0.14.1 // indirect + github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb // indirect + github.com/mattn/go-colorable v0.1.4 // indirect + github.com/mattn/go-isatty v0.0.10 // indirect + github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77 // indirect + github.com/oklog/run v1.0.0 // indirect + golang.org/x/net v0.0.0-20220722155237-a158d28d115b // indirect + golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f // indirect + golang.org/x/text v0.4.0 // indirect + google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 // indirect +) diff --git a/examples/code_generator/cmd/template_files/xx.plugin.go.tpl b/examples/code_generator/cmd/template_files/xx.plugin.go.tpl new file mode 100644 index 00000000..3251e4d2 --- /dev/null +++ b/examples/code_generator/cmd/template_files/xx.plugin.go.tpl @@ -0,0 +1,62 @@ +package {{.Package}} + +import ( + "context" + + "github.com/hashicorp/go-plugin" + + "google.golang.org/grpc" +) + +var Handshake = plugin.HandshakeConfig{ + ProtocolVersion: 1, + MagicCookieKey: "BASIC_PLUGIN", + MagicCookieValue: "hello", +} + +{{- range $item := .Services }} + +type {{.Name}}Plugin interface{ +{{$CurService := .Name}} +{{- range $item := .Methods }} + {{.Name}}(req *{{.InputType}}) *{{.OutputType}} +{{- end }} +} + +type GRPC{{.Name}} struct { + plugin.Plugin + Impl {{.Name}}Plugin +} + +func (p *GRPC{{.Name}}) GRPCServer(broker *plugin.GRPCBroker, server *grpc.Server) error { + Register{{.Name}}Server(server, &GPRC{{.Name}}ServerWrapper{impl: p.Impl}) + return nil +} + +func (p *GRPC{{.Name}}) GRPCClient(ctx context.Context, broker *plugin.GRPCBroker, conn *grpc.ClientConn) (interface{}, error) { + return &GRPC{{.Name}}ClientWrapper{client: New{{.Name}}Client(conn)}, nil +} + +type GPRC{{.Name}}ServerWrapper struct { + impl {{.Name}}Plugin + Unimplemented{{.Name}}Server +} + +{{$CurService := .Name}} +{{- range $item := .Methods }} +func (g *GPRC{{$CurService}}ServerWrapper) {{.Name}}(ctx context.Context, req *{{.InputType}}) (*{{.OutputType}}, error) { + return g.impl.{{.Name}}(req), nil +} +{{- end }} + +type GRPC{{.Name}}ClientWrapper struct { + client {{.Name}}Client +} + +{{- range $item := .Methods }} +func (g *GRPC{{$CurService}}ClientWrapper) {{.Name}}(ctx context.Context, req *{{.InputType}}) (*{{.OutputType}}, error) { + return g.client.{{.Name}}(ctx, req) +} +{{- end }} + +{{- end }} diff --git a/examples/code_generator/proto/my_test_grpc_plugin.proto b/examples/code_generator/proto/my_test_grpc_plugin.proto new file mode 100644 index 00000000..f4bce303 --- /dev/null +++ b/examples/code_generator/proto/my_test_grpc_plugin.proto @@ -0,0 +1,34 @@ +syntax = "proto3"; +package grpc_plugin; +option go_package = "github.com/hashicorp/go-plugin/examples/my_test_grpc_plugin"; + +import "google/protobuf/descriptor.proto"; +import "github.com/gogo/protobuf/gogoproto/gogo.proto"; + +extend google.protobuf.ServiceOptions { + optional string plugin_name = 51235; +} + +message Request{ + int32 field1 = 1; + int64 field2 = 2; + double field3 = 3; + string field4 = 4; + bytes field5 = 5; + repeated string field6 = 6; +} + +message Response { + int32 code = 1; + string msg = 2; +} + +service MyTestGrpcPlugin{ + option (plugin_name) = "my_plugin_1"; + rpc run(Request) returns (Response); +} + +service MyTestGrpcPluginForAnother{ + option (plugin_name) = "my_plugin_2"; + rpc ProcessData(Request) returns (Response); +}