Add parsesvcname package for getting service name

This commit is contained in:
Leland Batey 2016-12-19 17:06:30 -08:00
Родитель 56ab033558
Коммит 43e814950b
2 изменённых файлов: 200 добавлений и 0 удалений

Просмотреть файл

@ -0,0 +1,86 @@
package parsesvcname
import (
"io"
"io/ioutil"
"os"
"path/filepath"
"strings"
"github.com/TuneLab/go-truss/svcdef"
"github.com/TuneLab/go-truss/truss/execprotoc"
"github.com/pkg/errors"
)
// FromPaths accepts the paths of protobuf definition files and returns the
// name of the service in that protobuf definition file.
func FromPaths(gopath string, protoDefPaths []string) (string, error) {
td, err := ioutil.TempDir("", "parsesvcname")
defer os.RemoveAll(td)
if err != nil {
return "", errors.Wrap(err, "failed to create temporary directory for .pb.go files")
}
err = execprotoc.GeneratePBDotGo(protoDefPaths, []string{gopath}, td)
if err != nil {
return "", errors.Wrap(err, "failed to generate .pb.go files from protobuf definition files")
}
// Get path names of .pb.go files
pbgoPaths := []string{}
for _, p := range protoDefPaths {
base := filepath.Base(p)
barename := strings.TrimSuffix(base, filepath.Ext(p))
pbgp := filepath.Join(td, barename+".pb.go")
pbgoPaths = append(pbgoPaths, pbgp)
}
// Open all .pb.go files and store in map to be passed to svcdef.New()
openFiles := func(paths []string) (map[string]io.Reader, error) {
rv := map[string]io.Reader{}
for _, p := range paths {
reader, err := os.Open(p)
if err != nil {
return nil, errors.Wrapf(err, "cannot open file %q", p)
}
rv[p] = reader
}
return rv, nil
}
pbgoFiles, err := openFiles(pbgoPaths)
if err != nil {
return "", errors.Wrap(err, "cannot open all .pb.go files")
}
pbFiles, err := openFiles(protoDefPaths)
if err != nil {
return "", errors.Wrap(err, "cannot open all .proto files")
}
sd, err := svcdef.New(pbgoFiles, pbFiles)
if err != nil {
return "", errors.Wrapf(err, "failed to create service definition; did you pass ALL the protobuf files to truss?")
}
return sd.Service.Name, nil
}
func FromReaders(gopath string, protoDefReaders []io.Reader) (string, error) {
protoDir, err := ioutil.TempDir("", "parsesvcname-fromreaders")
if err != nil {
return "", errors.Wrap(err, "failed to create temporary directory for protobuf files")
}
// Ensures all the temporary files are removed
defer os.RemoveAll(protoDir)
protoDefPaths := []string{}
for _, rdr := range protoDefReaders {
f, err := ioutil.TempFile(protoDir, "parsesvcname-fromreader")
_, err = io.Copy(f, rdr)
if err != nil {
return "", errors.Wrap(err, "couldn't copy contents of our proto file into the os.File: ")
}
path := f.Name()
f.Close()
protoDefPaths = append(protoDefPaths, path)
}
return FromPaths(gopath, protoDefPaths)
}

Просмотреть файл

@ -0,0 +1,114 @@
package parsesvcname
import (
"io"
"io/ioutil"
"os"
"strings"
"testing"
)
// Provide a basic proto file to test that FromPaths returns the name of the
// service in the file at the provided path.
func TestFromPaths(t *testing.T) {
protoStr := `
syntax = "proto3";
package echo;
import "github.com/TuneLab/go-genproto/googleapis/api/serviceconfig/annotations.proto";
service BounceEcho {
rpc Echo (EchoRequest) returns (EchoResponse) {
option (google.api.http) = {
get: "/echo"
};
}
}
message EchoRequest {
string In = 1;
}
message EchoResponse {
string Out = 1;
}
`
protoDir, err := ioutil.TempDir("", "parsesvcname-test")
if err != nil {
t.Fatal("cannot create temp directory to store proto definition: ", err)
}
defer os.RemoveAll(protoDir)
f, err := ioutil.TempFile(protoDir, "trusstest")
_, err = io.Copy(f, strings.NewReader(protoStr))
if err != nil {
t.Fatal("couldn't copy contents of our proto file into the os.File: ", err)
}
path := f.Name()
f.Close()
svcname, err := FromPaths(os.Getenv("GOPATH"), []string{path})
if err != nil {
t.Fatal("failed to get service name from path: ", err)
}
if got, want := svcname, "BounceEcho"; got != want {
t.Fatalf("got != want; got = %q, want = %q", got, want)
}
}
// Provide a basic protobuf file to FromReader to ensure it returns the service
// name we expect.
func TestFromReader(t *testing.T) {
protoStr := `
syntax = "proto3";
package echo;
import "github.com/TuneLab/go-genproto/googleapis/api/serviceconfig/annotations.proto";
service BounceEcho {
rpc Echo (EchoRequest) returns (EchoResponse) {
option (google.api.http) = {
get: "/echo"
};
}
}
message EchoRequest {
string In = 1;
}
message EchoResponse {
string Out = 1;
}
`
svcname, err := FromReaders(os.Getenv("GOPATH"), []io.Reader{strings.NewReader(protoStr)})
if err != nil {
t.Fatal("failed to get service name from path: ", err)
}
if got, want := svcname, "BounceEcho"; got != want {
t.Fatalf("got != want; got = %q, want = %q", got, want)
}
}
// Ensure that passing a protobuf file that's not importing google annotations will function properly.
func TestNoAnnotations(t *testing.T) {
protoStr := `
syntax = "proto3";
package echo;
service BounceEcho {
rpc Echo (EchoRequest) returns (EchoResponse) {}
}
message EchoRequest {
string In = 1;
}
message EchoResponse {
string Out = 1;
}
`
svcname, err := FromReaders(os.Getenv("GOPATH"), []io.Reader{strings.NewReader(protoStr)})
if err != nil {
t.Fatal("failed to get service name from path: ", err)
}
if got, want := svcname, "BounceEcho"; got != want {
t.Fatalf("got != want; got = %q, want = %q", got, want)
}
}