Grpc Golang using UnimplementedServer with other embedded interfaces - go

I've recently updated to the latest protoc and Go plugins with an existing proto 3 codebase, and am running into trouble with the new UnimplementedServer functionality. The struct that is used for the Grpc server already embeds another interface describing the methods implemented by this service. After embedding the UnimplementedServer reference in my struct, I get an ambiguous error from the compiler and it tells me I am not implementing my service method anymore. Is there some issue with the way I have structured the code? Code to reproduce follows, with libprotoc 3.17.3, protoc-gen-go v1.27.1, and protoc-gen-go-grpc 1.1.0:
api/proto/core_service.proto:
package testbed;
option go_package = "internal/proto";
service Core {
rpc CreateThing(Thing) returns (ThingResponse) {};
}
message Thing {
string name = 1;
}
message ThingResponse {
int64 result = 1;
}
internal/core.go:
import (
"context"
"testbed.org/demo/internal/proto"
)
type CoreApi interface {
CreateThing(ctx context.Context, t *proto.Thing) (*proto.ThingResponse, error)
}
internal/core_endpoints.go:
import (
"context"
"testbed.org/demo/internal/proto"
)
type CoreEndpoints struct {}
func (ce CoreEndpoints) CreateThing(_ context.Context, _ *proto.Thing) (*proto.ThingResponse, error) {
return nil, nil
}
cmd/service.go:
import (
//"context"
. "testbed.org/demo/internal"
"testbed.org/demo/internal/proto"
"google.golang.org/grpc"
)
type MyServer struct {
CoreApi
proto.UnimplementedCoreServer
}
func main() {
mySvr := &MyServer{CoreApi: &CoreEndpoints{}}
//_, _ = mySvr.CoreApi.CreateThing(context.Background(), &proto.Thing{})
grpcSvr := grpc.NewServer()
proto.RegisterCoreServer(grpcSvr, mySvr)
}
Build:
protoc -I api/proto/ api/proto/*.proto --go_out=. --go-grpc_out=.
go build -o bin/svc cmd/service.go
cmd/service.go:19:26: MyServer.CreateThing is ambiguous
cmd/service.go:19:26: cannot use mySvr (type *MyServer) as type "testbed.org/demo/internal/proto".CoreServer in argument to "testbed.org/demo/internal/proto".RegisterCoreServer:
*MyServer does not implement "testbed.org/demo/internal/proto".CoreServer (missing CreateThing method)
gmake: *** [Makefile:8: service] Error 2

As the log says, 'Myserver' does not implement the coreserver interface.
type MyServer struct {
endpoint CoreEndpoints
proto.UnimplementedCoreServer
}
func (srv MyServer) CreateThing(ctx context.Context, in *proto.Thing)(*proto.ThingResponse, error) {
return srv.endpoint.CreateThing(ctx,in)
}

Related

Override Function from Another Module in Golang

How do you override a function created in another module in Golang?
Module A
In one module I have the function NewPersonApiService, the full code is laid out as below:
package openapi
import (
"context"
"errors"
"net/http"
)
// PersonApiService is a service that implements the logic for the PersonApiServicer
// This service should implement the business logic for every endpoint for the PersonApi API.
// Include any external packages or services that will be required by this service.
type PersonApiService struct {
}
// NewPersonApiService creates a default api service
func NewPersonApiService() PersonApiServicer {
return &PersonApiService{}
}
// ShowPerson - Detail
func (s *PersonApiService) ShowPerson(ctx context.Context) (ImplResponse, error) {
// TODO - update ShowPerson with the required logic for this service method.
// Add api_person_service.go to the .openapi-generator-ignore to avoid overwriting this service implementation when updating open api generation.
//TODO: Uncomment the next line to return response Response(200, Person{}) or use other options such as http.Ok ...
//return Response(200, Person{}), nil
//TODO: Uncomment the next line to return response Response(0, Error{}) or use other options such as http.Ok ...
//return Response(0, Error{}), nil
return Response(http.StatusNotImplemented, nil), errors.New("ShowPerson method not implemented")
}
Module B
In a separate module I want to override this NewPersonApiService.
I can call this function in the other module by doing the following:
package main
import (
"log"
"net/http"
openapi "build/code/spec/src"
)
func main() {
log.Printf("Server started")
PersonApiService := openapi.NewPersonApiService()
PersonApiController := openapi.NewPersonApiController(PersonApiService)
router := openapi.NewRouter(PersonApiController)
log.Fatal(http.ListenAndServe(":8080", router))
}
But if I try to override the function I get compilation error, unresolved type for openapi, below is what I was attempting to do:
package main
import (
"context"
"log"
"net/http"
openapi "build/code/spec/src"
)
func main() {
log.Printf("Server started")
PersonApiService := openapi.NewPersonApiService()
PersonApiController := openapi.NewPersonApiController(PersonApiService)
router := openapi.NewRouter(PersonApiController)
log.Fatal(http.ListenAndServe(":8080", router))
}
func (s openapi.PersonApiService) ShowPerson(ctx context.Context) (openapi.ImplResponse, error) {
return openapi.Response(200, openapi.Person{}), nil
}
Below is an image of the compilation error
Additional Info:
I believe Module B is properly referencing Module A.
Module A's go.mod file reads as follows:
module build/code/spec
go 1.13
require github.com/go-chi/chi/v5 v5.0.3
Module B's go.mod file reads as follows:
module bakkt.com/boilerplate
go 1.19
replace build/code/spec => ./../build/generated/
require build/code/spec v0.0.0-00010101000000-000000000000
require github.com/go-chi/chi/v5 v5.0.3 // indirect
The solution was to implement the ShowPerson method in another module, you would need to create a new type that implements the PersonApiServicer interface and provides its own implementation of the ShowPerson method.
Running this in Module B worked and allowed me to change the response of the API call defined in Module A.
package main
import (
"context"
"log"
"net/http"
openapi "build/code/spec/src"
)
type MyPersonApiService struct{}
func NewMyPersonApiService() openapi.PersonApiServicer {
return &MyPersonApiService{}
}
func (s *MyPersonApiService) ShowPerson(ctx context.Context) (openapi.ImplResponse, error) {
// TODO: Add your own implementation of the ShowPerson method here.
// For example, you could retrieve a person's details and return them as follows:
person := openapi.Person{Id: 23, Name: "Vark Thins", Age: 20}
return openapi.Response(http.StatusOK, person), nil
}
func main() {
log.Printf("Server started")
PersonApiService := NewMyPersonApiService()
PersonApiController := openapi.NewPersonApiController(PersonApiService)
router := openapi.NewRouter(PersonApiController)
log.Fatal(http.ListenAndServe(":8080", router))
}

Why can't I call an interface with a collection of methods from the main package

I am really new to golang and I am trying to see how encapsulation really works in go.
I have the following structure
-- package a
-a_core.go
-a.go
-models.go
-- main.go
In models.go I have structs for request and responses for an api call,
a.go has an empty struct, which is private and a public interface, which I want to expose with various methods
a_core.go just has some business logic which would be called in my interface implementation
Then, I have a main.go where I just call the public interface.
code in a.go
package a
type myFunction struct{}
type MyFunc interface {
Create(myData *MyData) (*MyData, error)
Fetch(test string)
Delete(test string)
}
//Concrete implementations that can be accessed publicly
func (a *myFunction) Create(data *MyData) (*MyData, error) {
return nil, nil
}
func (a *myFunction) Fetch(test string) {
}
func (a *myFunction) Delete(test string) {
}
In main.go, I call the interface my first create the MyData pointer with values
data := &a.MyData{
/////
}
result, err := a.MyFunc.Create(data)
I get the following error when I do this,
too few arguments in call to a.MyFunc.Create
cannot use data (variable of type *a.MyData) as a.MyFunc value in argument to a.MyFunc.Create: missing method CreatecompilerInvalidIfaceAssign
Please what am I doing wrong?
Here is an example
Note that names in uppercase are public, in lowercase private (see https://tour.golang.org/basics/3 )
./go-example/main.go
package main
import "go-example/animal"
func main() {
var a animal.Animal
a = animal.Lion{Age: 10}
a.Breathe()
a.Walk()
}
./go-example/animal/animal.go
package animal
import "fmt"
type Animal interface {
Breathe()
Walk()
}
type Lion struct {
Age int
}
func (l Lion) Breathe() {
fmt.Println("Lion breathes")
}
func (l Lion) Walk() {
fmt.Println("Lion walk")
}

gob: type not registered for interface: fasthbbftv1.BroadcastMessage

My code organization structure is like this
package_name/
server/
rpc_server.go
client_test.go
I register an RPC server to receive messages from other nodes.
type RPCMessageArgs struct {
Operation string
Info Message
}
type Message struct {
NodeID uint64
Payload interface{}
}
func init() {
gob.Register(BroadcastMessage{})
}
func main() {
// start rpc server
}
I start my RPC server in the server folder with the command go run rpc_server.go.
My test code in the client_test.go is as follows
package package_name
import (
"encoding/gob"
"log"
"net/rpc"
"strconv"
"testing"
)
type BroadcastMessage struct {
Payload interface{}
}
func TestClient(t *testing.T) {
gob.Register(BroadcastMessage{})
args := &RPCMessageArgs{}
args.Info.NodeID = 3
args.Info.Payload = &BroadcastMessage{}
reply := &RPCMessageReply{}
client, err := rpc.Dial("tcp", ":800"+strconv.Itoa(1))
if err != nil {
log.Fatal("dialing: ", err)
}
err = client.Call("RPCServer.RemoteCall", args, reply)
if err != nil {
log.Fatal("call error: ", err)
}
}
I get this error
call error: gob: name not registered for interface: "package_name.BroadcastMessage"
I made a very low-level mistake. I defined two type BroadcastMessage in two files, one in rpc_server.go and one in client_go.go. I should define these structures in a package, and then let these two files call these structures.
After the change, my code structure is as follows:
package_name/
args/
rpc_args.go
server/
rpc_server.go
client_test.go
Define the relevant structure in the rpc_args.go file, import the args package in other files, and then call the gob.Register() command in rpc_server.go and client_test.go to run normally.

How to create methods for string type in Golang

I'm planning to create some method extension for the string type.
I need to write all my extension methods into a separate package.
here is my hierarchy.
root
| main.go
| validation
| validate.go
on the main.go I would like to have, "abcd".Required()
main.go
package main
import (
"fmt"
"./validation"
)
func main() {
fmt.Println( "abcd".Required() )
}
validate.go
package validation
func (s string) Required() bool {
if s != "" {
return true
}
return false
}
When I run it, will get an error.
error
cannot define new methods on non-local type string
I found some answers in other questions on StackOverflow but they don't exactly talk about the string type & having the method in a different package file.
In your validate.go create a new type String:
package validation
type String string
func (s String) Required() bool {
return s != ""
}
And then work with validation.String objects in your main:
package main
import (
"fmt"
"./validation"
)
func main() {
fmt.Println(validation.String("abcd").Required())
}
An executable example with it all in the same file here:
https://play.golang.org/p/z_LcTZ6Qvfn

passing channel param to a dynamic loaded func in golang

I'm new to golang and golang plugins. I'm having trouble passing a chan object to this functions. If I switch to int it works. Not sure what exactly I am missing. Thanks for any help.
dataunit.go
package main
type DataUnit struct {
i int
s string
}
modcounter.go
package main
import (
"fmt"
// "strconv"
)
type module string
//func (m module) RunMod(i int) {
func (m module) RunMod(in <-chan *DataUnit) {
//fmt.Println("Hello Universe " + strconv.Itoa(i))
fmt.Println("Hello Universe ")
n := <-in
fmt.Printf(n.s)
}
var Module module
modmain.go
package main
import (
"fmt"
"os"
"plugin"
)
type DataUnit struct {
i int
s string
}
type Module interface {
//RunMod(i int)
RunMod(in <-chan *DataUnit)
}
func main() {
out := make(chan *DataUnit, 2000)
plug, err := plugin.Open("./modcounter.so")
if err != nil {
fmt.Printf("FATAL (plugin.Open): " + err.Error())
os.Exit(1)
}
symModule, err := plug.Lookup("Module")
if err != nil {
fmt.Printf(err.Error())
os.Exit(1)
}
var module Module
module, ok:= symModule.(Module)
if !ok {
fmt.Println("unexpected type from module symbol")
os.Exit(1)
}
//module.RunMod(5)
module.RunMod(out)
}
go build -buildmode=plugin -o modcounter.so modcounter.go dataunit.go
go build modmain.go dataunit.go
./modmain
unexpected type from module symbol
If you are still learning golang then plugins are definitely not the place to start. I believe plugin support is still experimental and doesn't work on all OS - in 1.10 I believe Darwin was added to previous Linux.
A quick search reveals a pre-existing issue reported similar to yours -looks like it's in go 1.10:
https://github.com/golang/go/issues/24351
You can maybe track through the repository and get a branch or version where this is fixed, but whilst your learning you aren't going to have any confidence in whether problems with this area of functionality are with your code or the plugin system. I recommend therefore sticking to learning go with core language features for now.
In go is possible to pass channels of objects!!
For example, if you to use <-chan http.Header will works fine. The question is that the params must be shared between modules and application. So if you reallocate DataUnit for another package will work.
My test was structured like:
My interface:
//in modmain.go
type Module interface {
RunMod(in <-chan *mydata.DataUnit)
}
My module:
//in modcounter.go
func (m module) RunMod(in <-chan *mydata.DataUnit) {
fmt.Println("Hello Universe ")
n := <-in
fmt.Printf("%v", n.S)
}
My data:
//in dataunit.go
type DataUnit struct {
I int //export field
S string //export field
}
The result:
P.S.: Docker with golang 1.10 was used for tests.
#in Dockerfile
FROM golang:1.10
COPY . /go/
RUN export GOPATH=$GOPATH:/go/
RUN cd /go/src/mydata && go build dataunit.go
RUN cd /go/src/app && go build modmain.go
RUN cd /go/src/app && go build -buildmode=plugin -o modcounter.so modcounter.go
WORKDIR /go/src/app
RUN ls -l
CMD ["/go/src/app/modmain"]

Resources