protoeasy

package module
v0.0.0-...-c2daa73 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Dec 23, 2017 License: MIT Imports: 22 Imported by: 0

README

CircleCI Go Report Card Docker Repository on Quay GoDoc MIT License

NEWS: Protocafe

I'm currently working on a protocol buffer package management tool and hosting service, similar to RubyGems, that uses components of protoeasy. Feel free to follow development and help out:

http://proto.cafe
https://github.com/protocafe/protocafe

Protoeasy

Protoeasy is intended to make compiling protocol buffers files easier, and to optionally offload the compilation to a server for consistency and so that protoc and any associated plugins do not have to be installed locally.

Instead of having to specify individual plugins and files, protoeasy operates on the concept of languages and directories. Compilation is recommended to happen in a provided docker container. The primary language developed for protoeasy was Go, and all gogo plugins are supported as well, but support also exists for C++, C#, Objective-C, Python, and Ruby. gRPC support is built in for all supported languages. Proto3 is used as the default compiler, however a proto2 docker container is also available, but support is limited.

Eventually, it would be nice to turn this into a full hosted service. If you would like to help, let me know :)

Quick Start

This assumes you are familiar with Go. I would recommend using Go 1.6.0.

Note that client/server mode is optional - you can use protoeasy locally if you have protoc and all relevant plugins installed.

# install binary
go get -v go.pedge.io/protoeasy/cmd/protoeasy
# OPTIONAL - if you have all the required binaries installed (protoc, plugins, etc), then you can use protoeasy locally
docker pull quay.io/pedge/protoeasy
docker run -d -p 6789:6789 quay.io/pedge/protoeasy
# Assuming you are running the docker daemon locally, DOCKER_ADDRESS is 0.0.0.0.
# Assuming you are running the docker daemon in a VM, DOCKER_ADDRESS is DOCKER_HOST without the port.
# Note that if you are running the docker daemon in a VM or remotely, port 6789 needs to be open for you to connect to
# DO NOT SET IF RUNNING LOCALLY
export PROTOEASY_ADDRESS="${DOCKER_ADDRESS}:6789"
# doing go as an example
# you do not need to cd in, but this is as an example
cd github.com/user/your-go-project
# this assumes your protocol buffers import paths are relative to the root of your go project
protoeasy --go --grpc --go-import-path github.com/user/your-go-project .
# compile for c++ and ruby too
protoeasy --go --grpc --go-import-path github.com/user/your-go-project --cpp --ruby .
# exclude protocol buffers files in foo/*
protoeasy --go --grpc --go-import-path github.com/user/your-go-project --exclude foo .
# this assumes your protocol buffers files are in ext/proto, and import paths are relative to ext/proto
protoeasy --go --grpc --go-import-path github.com/user/your-go-project/ext/proto ext/proto
# this assumes you have a protoeasy.yaml file in your current directory, see the file here as an example
protoeasy

Motivation

Have you ever had one of the following issues:

  • You're the resident protobuf expert on your team, and every time there is a change to a protocol buffers package or you get a new teammate, you already know what the next hour of your life will be.
  • You move to a new development machine, and can't remember how you got protoc installed properly, and now your old build scripts do not work anymore.
  • You upgrade proto3 on your computer, and suddenly protoc is broken.
  • One of your teammates upgrades proto3 on their computer, and suddently protoc is broken.
  • You did go get -u google.golang.org/grpc, or even worse, google.golang.org is a dependency of some package and is updated, and suddenly new, incompatible .pb.go files are created.
  • You have gRPC working on your mac, but linux is a mess.
  • You use Mosh and proto3, but since Mosh is still on proto2, you're in trouble.
  • You're sick of resolving all relative paths to absolute paths or vice versa for -I.
  • You really don't like having to edit a Makefile or script every time you add a protocol buffer file or package.
  • You never can figure out a good scheme for a large amount of protocol buffers files and how to do imports properly.
  • You think this protobuf thing is really cool, but are sick of maintaining large bash scripts or Makefile directives to use protoc, and are sick of having to get everything installed on your development machine, and you know there must be an easier way to do it but haven't found it yet.

Then protoeasy is for you!

Tutorial

Installation

Install protoeasy using make install, assuming ${GOPATH}/bin is on your ${PATH}. You can also do go get -v go.pedge.io/protoeasy/cmd/protoeasy.

Basics

Protoeasy compiles entire directories of protocol buffer files, as opposed to individual files. To use protoeasy:

  • All protocol buffers files in the same sub-directory must have the same protocol buffers package.
  • All imports must be relative to the base directory.

Both of these are already good practices. Assuming this structure, all you have to do is pass in a base directory, and the rest is done for you. All -I directives are automatically figured out, and all protocol buffers files in the same sub-directory are compiled together.

Instead of specifying individual output directives, protoeasy breaks compilation into separate languages, optionally doing gRPC compilation for each language.

Assume we have a single file in our current directory called foo.proto.

syntax = "proto3";

package foo;

message One {
  int64 i = 1;
}

To compile for ruby, we would do:

protoeasy --ruby .
#protoc \
#  -I/tmp/protoeasy-input353662656 \
#  -I/go/src/go.pedge.io/protoeasy/vendor/go.pedge.io/pb/proto \
#  --ruby_out=/tmp/protoeasy-output452425503 \
#  /tmp/protoeasy-input353662656/foo.proto

To compile for ruby and c++, we would do:

protoeasy --ruby --cpp .
#protoc \
#  -I/tmp/protoeasy-input138072562 \
#  -I/go/src/go.pedge.io/protoeasy/vendor/go.pedge.io/pb/proto \
#  --cpp_out=/tmp/protoeasy-output084691625 \
#  /tmp/protoeasy-input138072562/foo.proto
#protoc \
#  -I/tmp/protoeasy-input138072562 \
#  -I/go/src/go.pedge.io/protoeasy/vendor/go.pedge.io/pb/proto \
#  --ruby_out=/tmp/protoeasy-output084691625 \
#  /tmp/protoeasy-input138072562/foo.proto

If we wanted to generate gRPC code for both, we would do:

protoeasy --ruby --cpp --grpc .
#protoc \
#  -I/tmp/protoeasy-input924418036 \
#  -I/go/src/go.pedge.io/protoeasy/vendor/go.pedge.io/pb/proto \
#  --cpp_out=/tmp/protoeasy-output335846083 \
#  --grpc_out=/tmp/protoeasy-output335846083 \
#  --plugin=protoc-gen-grpc=/usr/local/bin/grpc_cpp_plugin \
#  /tmp/protoeasy-input924418036/foo.proto
#protoc \
#  -I/tmp/protoeasy-input924418036 \
#  -I/go/src/go.pedge.io/protoeasy/vendor/go.pedge.io/pb/proto \
#  --ruby_out=/tmp/protoeasy-output335846083 \
#  --grpc_out=/tmp/protoeasy-output335846083 \
#  --plugin=protoc-gen-grpc=/usr/local/bin/grpc_ruby_plugin \
#  /tmp/protoeasy-input924418036/foo.proto

What is going on here:

  • For each language, a protoc command is generated. Each language is run separately because some protoc flags overlap (--plugin specifically).
  • The /tmp/protoeasy-input... directories are the directory where foo.proto is located. These compilations were done on a remote server, explained later.
  • If --grpc is specified, --grpc_out and --plugin flags are specified for each associated language, except for Go which has different logic.
  • Other typical imports are added automatically, explained later.

Supported languages are C++, C#, Objective-C, Python, Ruby, and Go. Protoeasy was primarily developed for Go (which is what I use it for), so if there are idioms for other languages, let me know.

Go

Go has special handling. Let's assume we have our original foo.proto, and another file bar/bar.proto in the sub-directory bar, and let's assume these are in the Go package github.com/alice/helloworld.

syntax = "proto3";

import "google/api/annotations.proto";
import "google/protobuf/empty.proto";
import "foo.proto";

package bar;

message Two {
  foo.One one = 1;
  int64 j = 2;
}

service API {
  rpc Do(Two) returns (google.protobuf.Empty) {
    option (google.api.http) = {
      post: "/do"
      body: "*"
    };
  }
}

Let's compile these with go, grpc, and grpc-gateway, and do them all now without building up the example so that this README does not get too long.

protoeasy --go --go-import-path=github.com/alice/helloworld --grpc --grpc-gateway .
#protoc \
#  -I/tmp/protoeasy-input035188496 \
#  -I/go/src/go.pedge.io/protoeasy/vendor/go.pedge.io/pb/proto \
#  --go_out=Mbar/bar.proto=github.com/alice/helloworld/bar,Mfoo.proto=github.com/alice/helloworld,Mgoogle/api/annotations.proto=github.com/grpc-ecosystem/grpc-gateway/third_party/pb/go/google/api,Mgoogle/api/http.proto=github.com/grpc-ecosystem/grpc-gateway/third_party/pb/go/google/api,Mgoogle/datastore/v1beta3/datastore.proto=go.pedge.io/pb/go/google/datastore/v1beta3,Mgoogle/datastore/v1beta3/entity.proto=go.pedge.io/pb/go/google/datastore/v1beta3,Mgoogle/datastore/v1beta3/query.proto=go.pedge.io/pb/go/google/datastore/v1beta3,Mgoogle/devtools/cloudtrace/v1/trace.proto=go.pedge.io/pb/go/google/devtools/cloudtrace/v1,Mgoogle/example/library/v1/library.proto=go.pedge.io/pb/go/google/example/library/v1,Mgoogle/iam/v1/iam_policy.proto=go.pedge.io/pb/go/google/iam/v1,Mgoogle/iam/v1/policy.proto=go.pedge.io/pb/go/google/iam/v1,Mgoogle/longrunning/operations.proto=go.pedge.io/pb/go/google/longrunning,Mgoogle/protobuf/any.proto=go.pedge.io/pb/go/google/protobuf,Mgoogle/protobuf/api.proto=go.pedge.io/pb/go/google/protobuf,Mgoogle/protobuf/descriptor.proto=github.com/golang/protobuf/protoc-gen-go/descriptor,Mgoogle/protobuf/duration.proto=go.pedge.io/pb/go/google/protobuf,Mgoogle/protobuf/empty.proto=go.pedge.io/pb/go/google/protobuf,Mgoogle/protobuf/field_mask.proto=go.pedge.io/pb/go/google/protobuf,Mgoogle/protobuf/source_context.proto=go.pedge.io/pb/go/google/protobuf,Mgoogle/protobuf/struct.proto=go.pedge.io/pb/go/google/protobuf,Mgoogle/protobuf/timestamp.proto=go.pedge.io/pb/go/google/protobuf,Mgoogle/protobuf/type.proto=go.pedge.io/pb/go/google/protobuf,Mgoogle/protobuf/wrappers.proto=go.pedge.io/pb/go/google/protobuf,Mgoogle/pubsub/v1/pubsub.proto=go.pedge.io/pb/go/google/pubsub/v1,Mgoogle/pubsub/v1beta2/pubsub.proto=go.pedge.io/pb/go/google/pubsub/v1beta2,Mgoogle/rpc/code.proto=go.pedge.io/pb/go/google/rpc,Mgoogle/rpc/error_details.proto=go.pedge.io/pb/go/google/rpc,Mgoogle/rpc/status.proto=go.pedge.io/pb/go/google/rpc,Mgoogle/type/color.proto=go.pedge.io/pb/go/google/type,Mgoogle/type/date.proto=go.pedge.io/pb/go/google/type,Mgoogle/type/dayofweek.proto=go.pedge.io/pb/go/google/type,Mgoogle/type/latlng.proto=go.pedge.io/pb/go/google/type,Mgoogle/type/money.proto=go.pedge.io/pb/go/google/type,Mgoogle/type/timeofday.proto=go.pedge.io/pb/go/google/type,plugins=grpc:/tmp/protoeasy-output215726383 \
#  --grpc-gateway_out=Mbar/bar.proto=github.com/alice/helloworld/bar,Mfoo.proto=github.com/alice/helloworld,Mgoogle/api/annotations.proto=github.com/grpc-ecosystem/grpc-gateway/third_party/pb/go/google/api,Mgoogle/api/http.proto=github.com/grpc-ecosystem/grpc-gateway/third_party/pb/go/google/api,Mgoogle/datastore/v1beta3/datastore.proto=go.pedge.io/pb/go/google/datastore/v1beta3,Mgoogle/datastore/v1beta3/entity.proto=go.pedge.io/pb/go/google/datastore/v1beta3,Mgoogle/datastore/v1beta3/query.proto=go.pedge.io/pb/go/google/datastore/v1beta3,Mgoogle/devtools/cloudtrace/v1/trace.proto=go.pedge.io/pb/go/google/devtools/cloudtrace/v1,Mgoogle/example/library/v1/library.proto=go.pedge.io/pb/go/google/example/library/v1,Mgoogle/iam/v1/iam_policy.proto=go.pedge.io/pb/go/google/iam/v1,Mgoogle/iam/v1/policy.proto=go.pedge.io/pb/go/google/iam/v1,Mgoogle/longrunning/operations.proto=go.pedge.io/pb/go/google/longrunning,Mgoogle/protobuf/any.proto=go.pedge.io/pb/go/google/protobuf,Mgoogle/protobuf/api.proto=go.pedge.io/pb/go/google/protobuf,Mgoogle/protobuf/descriptor.proto=github.com/golang/protobuf/protoc-gen-go/descriptor,Mgoogle/protobuf/duration.proto=go.pedge.io/pb/go/google/protobuf,Mgoogle/protobuf/empty.proto=go.pedge.io/pb/go/google/protobuf,Mgoogle/protobuf/field_mask.proto=go.pedge.io/pb/go/google/protobuf,Mgoogle/protobuf/source_context.proto=go.pedge.io/pb/go/google/protobuf,Mgoogle/protobuf/struct.proto=go.pedge.io/pb/go/google/protobuf,Mgoogle/protobuf/timestamp.proto=go.pedge.io/pb/go/google/protobuf,Mgoogle/protobuf/type.proto=go.pedge.io/pb/go/google/protobuf,Mgoogle/protobuf/wrappers.proto=go.pedge.io/pb/go/google/protobuf,Mgoogle/pubsub/v1/pubsub.proto=go.pedge.io/pb/go/google/pubsub/v1,Mgoogle/pubsub/v1beta2/pubsub.proto=go.pedge.io/pb/go/google/pubsub/v1beta2,Mgoogle/rpc/code.proto=go.pedge.io/pb/go/google/rpc,Mgoogle/rpc/error_details.proto=go.pedge.io/pb/go/google/rpc,Mgoogle/rpc/status.proto=go.pedge.io/pb/go/google/rpc,Mgoogle/type/color.proto=go.pedge.io/pb/go/google/type,Mgoogle/type/date.proto=go.pedge.io/pb/go/google/type,Mgoogle/type/dayofweek.proto=go.pedge.io/pb/go/google/type,Mgoogle/type/latlng.proto=go.pedge.io/pb/go/google/type,Mgoogle/type/money.proto=go.pedge.io/pb/go/google/type,Mgoogle/type/timeofday.proto=go.pedge.io/pb/go/google/type:/tmp/protoeasy-output215726383 \
#  /tmp/protoeasy-input035188496/bar/bar.proto
#protoc \
#  -I/tmp/protoeasy-input035188496 \
#  -I/go/src/go.pedge.io/protoeasy/vendor/go.pedge.io/pb/go/google/protobuf \
#  --go_out=Mbar/bar.proto=github.com/alice/helloworld/bar,Mfoo.proto=github.com/alice/helloworld,Mgoogle/api/annotations.proto=github.com/grpc-ecosystem/grpc-gateway/third_party/pb/go/google/api,Mgoogle/api/http.proto=github.com/grpc-ecosystem/grpc-gateway/third_party/pb/go/google/api,Mgoogle/datastore/v1beta3/datastore.proto=go.pedge.io/pb/go/google/datastore/v1beta3,Mgoogle/datastore/v1beta3/entity.proto=go.pedge.io/pb/go/google/datastore/v1beta3,Mgoogle/datastore/v1beta3/query.proto=go.pedge.io/pb/go/google/datastore/v1beta3,Mgoogle/devtools/cloudtrace/v1/trace.proto=go.pedge.io/pb/go/google/devtools/cloudtrace/v1,Mgoogle/example/library/v1/library.proto=go.pedge.io/pb/go/google/example/library/v1,Mgoogle/iam/v1/iam_policy.proto=go.pedge.io/pb/go/google/iam/v1,Mgoogle/iam/v1/policy.proto=go.pedge.io/pb/go/google/iam/v1,Mgoogle/longrunning/operations.proto=go.pedge.io/pb/go/google/longrunning,Mgoogle/protobuf/any.proto=go.pedge.io/pb/go/google/protobuf,Mgoogle/protobuf/api.proto=go.pedge.io/pb/go/google/protobuf,Mgoogle/protobuf/descriptor.proto=github.com/golang/protobuf/protoc-gen-go/descriptor,Mgoogle/protobuf/duration.proto=go.pedge.io/pb/go/google/protobuf,Mgoogle/protobuf/empty.proto=go.pedge.io/pb/go/google/protobuf,Mgoogle/protobuf/field_mask.proto=go.pedge.io/pb/go/google/protobuf,Mgoogle/protobuf/source_context.proto=go.pedge.io/pb/go/google/protobuf,Mgoogle/protobuf/struct.proto=go.pedge.io/pb/go/google/protobuf,Mgoogle/protobuf/timestamp.proto=go.pedge.io/pb/go/google/protobuf,Mgoogle/protobuf/type.proto=go.pedge.io/pb/go/google/protobuf,Mgoogle/protobuf/wrappers.proto=go.pedge.io/pb/go/google/protobuf,Mgoogle/pubsub/v1/pubsub.proto=go.pedge.io/pb/go/google/pubsub/v1,Mgoogle/pubsub/v1beta2/pubsub.proto=go.pedge.io/pb/go/google/pubsub/v1beta2,Mgoogle/rpc/code.proto=go.pedge.io/pb/go/google/rpc,Mgoogle/rpc/error_details.proto=go.pedge.io/pb/go/google/rpc,Mgoogle/rpc/status.proto=go.pedge.io/pb/go/google/rpc,Mgoogle/type/color.proto=go.pedge.io/pb/go/google/type,Mgoogle/type/date.proto=go.pedge.io/pb/go/google/type,Mgoogle/type/dayofweek.proto=go.pedge.io/pb/go/google/type,Mgoogle/type/latlng.proto=go.pedge.io/pb/go/google/type,Mgoogle/type/money.proto=go.pedge.io/pb/go/google/type,Mgoogle/type/timeofday.proto=go.pedge.io/pb/go/google/type,plugins=grpc:/tmp/protoeasy-output215726383 \
#  --grpc-gateway_out=Mbar/bar.proto=github.com/alice/helloworld/bar,Mfoo.proto=github.com/alice/helloworld,Mgoogle/api/annotations.proto=github.com/grpc-ecosystem/grpc-gateway/third_party/pb/go/google/api,Mgoogle/api/http.proto=github.com/grpc-ecosystem/grpc-gateway/third_party/pb/go/google/api,Mgoogle/datastore/v1beta3/datastore.proto=go.pedge.io/pb/go/google/datastore/v1beta3,Mgoogle/datastore/v1beta3/entity.proto=go.pedge.io/pb/go/google/datastore/v1beta3,Mgoogle/datastore/v1beta3/query.proto=go.pedge.io/pb/go/google/datastore/v1beta3,Mgoogle/devtools/cloudtrace/v1/trace.proto=go.pedge.io/pb/go/google/devtools/cloudtrace/v1,Mgoogle/example/library/v1/library.proto=go.pedge.io/pb/go/google/example/library/v1,Mgoogle/iam/v1/iam_policy.proto=go.pedge.io/pb/go/google/iam/v1,Mgoogle/iam/v1/policy.proto=go.pedge.io/pb/go/google/iam/v1,Mgoogle/longrunning/operations.proto=go.pedge.io/pb/go/google/longrunning,Mgoogle/protobuf/any.proto=go.pedge.io/pb/go/google/protobuf,Mgoogle/protobuf/api.proto=go.pedge.io/pb/go/google/protobuf,Mgoogle/protobuf/descriptor.proto=github.com/golang/protobuf/protoc-gen-go/descriptor,Mgoogle/protobuf/duration.proto=go.pedge.io/pb/go/google/protobuf,Mgoogle/protobuf/empty.proto=go.pedge.io/pb/go/google/protobuf,Mgoogle/protobuf/field_mask.proto=go.pedge.io/pb/go/google/protobuf,Mgoogle/protobuf/source_context.proto=go.pedge.io/pb/go/google/protobuf,Mgoogle/protobuf/struct.proto=go.pedge.io/pb/go/google/protobuf,Mgoogle/protobuf/timestamp.proto=go.pedge.io/pb/go/google/protobuf,Mgoogle/protobuf/type.proto=go.pedge.io/pb/go/google/protobuf,Mgoogle/protobuf/wrappers.proto=go.pedge.io/pb/go/google/protobuf,Mgoogle/pubsub/v1/pubsub.proto=go.pedge.io/pb/go/google/pubsub/v1,Mgoogle/pubsub/v1beta2/pubsub.proto=go.pedge.io/pb/go/google/pubsub/v1beta2,Mgoogle/rpc/code.proto=go.pedge.io/pb/go/google/rpc,Mgoogle/rpc/error_details.proto=go.pedge.io/pb/go/google/rpc,Mgoogle/rpc/status.proto=go.pedge.io/pb/go/google/rpc,Mgoogle/type/color.proto=go.pedge.io/pb/go/google/type,Mgoogle/type/date.proto=go.pedge.io/pb/go/google/type,Mgoogle/type/dayofweek.proto=go.pedge.io/pb/go/google/type,Mgoogle/type/latlng.proto=go.pedge.io/pb/go/google/type,Mgoogle/type/money.proto=go.pedge.io/pb/go/google/type,Mgoogle/type/timeofday.proto=go.pedge.io/pb/go/google/type:/tmp/protoeasy-output215726383 \
#  /tmp/protoeasy-input035188496/foo.proto

Whoa! There's a lot going on there, let's walk through it:

  • The -I directives are the same as other languages.
  • For gRPC, instead of having --grpc_out and --plugin, Go does --go_out=plugins=grpc
  • We specified that we are in the github.com/alice/helloworld package using --go-import-path and the modifiers Mbar/bar.proto=github.com/alice/helloworld/bar,Mfoo.proto=github.com/alice/helloworld were generated for us. Therefore bar.pb.go has the import statement import foo "github.com/alice/helloworld". If, instead, all protocol buffers files were in a sub-directory proto in github.com/alice/helloworld, we would have done protoeasy --go --grpc --grpc_gateway --go-import-path github.com/alice/helloworld/proto proto
  • We have the modifiers for a bunch of other files, but specifically Mgoogle/api/annotations.proto=github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis/google/api,Mgoogle/protobuf/empty.proto=go.pedge.io/pb/go. More on this later.
  • The directive --grpc-gateway_out created a bar/bar.pb.gw.go file for us, for grpc-gateway. You should really check grpc-gateway out :)

Other Go flags:

  • --go-protoc-plugin=: specifiy a plugin other than protoc-gen-go. This is useful for gogo.
  • --go-modifier: specify an additional modifier of the form Mfile=package.
  • --go-no-default-modifiers: do not use the default modifiers for google/protobuf and googleapis.

Automatically Imported Packages

Protoeasy automatically imports the protocol buffers files in:

Track https://github.com/golang/protobuf/issues/50 for more details.

More Examples

See make proto and make example-complete for two more example usages. I also use protoeasy for all my public Go projects on GitHub that use protocol buffers (the command using protoeasy is in the Makefile in each project):

Client/Server Setup

Protoeasy works on your local machine by default, however this is not how it is intended to be used. Protoeasy provides a Docker image quay.io/pedge/protoeasy, created from the Dockerfile in this repository, that has everything necessary for compilation already installed, and which runs the protoeasyd daemon. The protoeasy binary will automatically connect to this daemon and delegate compilation if the environment variable PROTOEASY_ADDRESS is set.

How this works:

  • The client (protoeasy) tarballs all .proto files in the specified directory and uploads this tarball to the server, along with the directives given from the flags.
  • The server creates two temporary directories, one for the uploaded tarball, and one to output generated files.
  • The server untars the uploaded tarball, runs the protoeasy logic, and outputs the generated files.
  • The server tarballs the generated files, and sends them back to the client.
  • The client untars the generated files in the specified directory.

To use this, first get the Docker image:

docker pull quay.io/pedge/protoeasy

Or, build it from this repository:

make docker-build

Then launch the daemon:

docker run -d -p 6789:6789 quay.io/pedge/protoeasy # or do make docker-launch

My local setup is a Macbook Pro with Docker running in a VirtualBox VM with a private network at 192.168.10.10.

# a simple version from my Vagrantfile at https://github.com/peter-edge/dotfiles/blob/master/vagrant/common/base_provision.rb
Vagrant.configure(2) do |config|
  ...
  config.vm.network "private_network", ip: "192.168.10.10"
end
# on my vagrant host, this was setup using https://github.com/peter-edge/dotfiles/blob/master/bin/setup_docker_http_upstart.sh
ps -ef | grep docker
#root     16450     1  0 Nov28 ?        00:06:01 /usr/bin/docker daemon -H tcp://0.0.0.0:2375 -H unix:///var/run/docker.sock

Many Docker VirtualBox setups are similar to this, including I believe the one created from Docker Machine.

In my ~/.bash_aliases, I have:

export PROTOEASY_ADDRESS=192.168.10.10:6789

launch-protoeasy() {
  docker rm -f protoeasy || true
  docker run -d -p 6789:6789 --name=protoeasy quay.io/pedge/protoeasy
}

Whenever protoeasy is stopped, or when I want to restart it, I just run launch-protoeasy. Since I have PROTOEASY_ADDRESS exported, the protoeasy binary always connects to the running Docker container.

In this manner, I never actually compile protocol buffers files on my Macbook directly.

Q: Why not just do your builds directly in a Docker container, i.e. docker run --volume $(pwd):/compile --workdir /compile pedge/proto3grpc protoc --go_out=. foo.proto https://hub.docker.com/r/pedge/proto3grpc
A: This is what I used to do, but if your Docker host is NOT local (i.e. you are not using Docker locally in Linux, or in a local VM), this means that you cannot have a host volume linking. Protoeasy should soon have a deployment option once I get TLS added. Also, protoeasy is a little faster, but it's not really important.

Future Work

Protoeasy is brand new, and has a long way to go. Main areas:

  • Make sure compilation for non-Go languages is correct.
  • Get security added for public deployment.
  • Get an easy deployment option (probably Kubernetes) added.

If you want to help, let me know!

Documentation

Overview

Package protoeasy is intended to make using protoc simpler.

Protoeasy compiles all protocol buffer files in a directory/subdirectories, taking care of all include directories, takes care of gRPC compilation, and take care of package import modifiers for Golang.

Protoeasy also provides a client/server model where compilation is delegated to a server process meant to be run in a Docker container. This allows you to not have to install protoc, gRPC, and associated protoc plugins on your local development machine.

See the README.md file for more details.

Index

Constants

This section is empty.

Variables

View Source
var (
	//DefaultServerCompiler is the default Compiler for a server.
	DefaultServerCompiler = NewServerCompiler(CompilerOptions{})
	// DefaultAPIServer is the default API Server.
	DefaultAPIServer = NewAPIServer(DefaultServerCompiler, APIServerOptions{})
	// DefaultClientCompiler is the default Compiler for a client.
	DefaultClientCompiler = NewClientCompiler(
		NewLocalAPIClient(
			NewAPIServer(
				DefaultServerCompiler,
				APIServerOptions{
					NoLogging: true,
				},
			),
		),
		CompilerOptions{},
	)
	// DefaultDescriptorSetFileName is the default descriptor set file name.
	DefaultDescriptorSetFileName = "descriptor-set.pb"
	// DefaultFileCompileOptionsFile is the default file to get FileCompileOptions from.
	DefaultFileCompileOptionsFile = "protoeasy.yaml"
	// FileCompileOptionsVersionToCompileOptionsFunc is a map from FileCompileOptions version to CompileOptions converter function.
	FileCompileOptionsVersionToCompileOptionsFunc = map[string]func(*FileCompileOptions) (*CompileOptions, error){
		"v1": toCompileOptionsV1,
	}
)
View Source
var GoPluginType_name = map[int32]string{
	0: "GO_PLUGIN_TYPE_NONE",
	1: "GO_PLUGIN_TYPE_GO",
	2: "GO_PLUGIN_TYPE_GOFAST",
}
View Source
var GoPluginType_value = map[string]int32{
	"GO_PLUGIN_TYPE_NONE":   0,
	"GO_PLUGIN_TYPE_GO":     1,
	"GO_PLUGIN_TYPE_GOFAST": 2,
}
View Source
var GogoPluginType_name = map[int32]string{
	0: "GOGO_PLUGIN_TYPE_NONE",
	1: "GOGO_PLUGIN_TYPE_GOGO",
	2: "GOGO_PLUGIN_TYPE_GOGOFAST",
	3: "GOGO_PLUGIN_TYPE_GOGOFASTER",
	4: "GOGO_PLUGIN_TYPE_GOGOSLICK",
}
View Source
var GogoPluginType_value = map[string]int32{
	"GOGO_PLUGIN_TYPE_NONE":       0,
	"GOGO_PLUGIN_TYPE_GOGO":       1,
	"GOGO_PLUGIN_TYPE_GOGOFAST":   2,
	"GOGO_PLUGIN_TYPE_GOGOFASTER": 3,
	"GOGO_PLUGIN_TYPE_GOGOSLICK":  4,
}

Functions

func AllGoPluginTypeSimpleStrings

func AllGoPluginTypeSimpleStrings() []string

AllGoPluginTypeSimpleStrings returns the simple values for all GoPluginTypes.

func AllGogoPluginTypeSimpleStrings

func AllGogoPluginTypeSimpleStrings() []string

AllGogoPluginTypeSimpleStrings returns the simple values for all GogoPluginTypes.

func RegisterAPIServer

func RegisterAPIServer(s *grpc.Server, srv APIServer)

Types

type APIClient

type APIClient interface {
	Compile(ctx context.Context, in *CompileRequest, opts ...grpc.CallOption) (*CompileResponse, error)
}

func NewAPIClient

func NewAPIClient(cc *grpc.ClientConn) APIClient

func NewLocalAPIClient

func NewLocalAPIClient(apiServer APIServer) APIClient

NewLocalAPIClient returns a new APIClient that calls the APIServer directly.

type APIServer

type APIServer interface {
	Compile(context.Context, *CompileRequest) (*CompileResponse, error)
}

func NewAPIServer

func NewAPIServer(compiler Compiler, options APIServerOptions) APIServer

NewAPIServer returns a new APIServer for the given Compiler.

type APIServerOptions

type APIServerOptions struct {
	NoLogging bool
}

APIServerOptions are options for an APIServer.

type Command

type Command struct {
	Arg []string `protobuf:"bytes,1,rep,name=arg" json:"arg,omitempty"`
}

func (*Command) Descriptor

func (*Command) Descriptor() ([]byte, []int)

func (*Command) GetArg

func (m *Command) GetArg() []string

func (*Command) ProtoMessage

func (*Command) ProtoMessage()

func (*Command) Reset

func (m *Command) Reset()

func (*Command) String

func (m *Command) String() string

type CompileInfo

type CompileInfo struct {
	Command         []*Command                `protobuf:"bytes,1,rep,name=command" json:"command,omitempty"`
	InputSizeBytes  uint64                    `protobuf:"varint,2,opt,name=input_size_bytes,json=inputSizeBytes" json:"input_size_bytes,omitempty"`
	OutputSizeBytes uint64                    `protobuf:"varint,3,opt,name=output_size_bytes,json=outputSizeBytes" json:"output_size_bytes,omitempty"`
	Duration        *google_protobuf.Duration `protobuf:"bytes,4,opt,name=duration" json:"duration,omitempty"`
}

func (*CompileInfo) Descriptor

func (*CompileInfo) Descriptor() ([]byte, []int)

func (*CompileInfo) GetCommand

func (m *CompileInfo) GetCommand() []*Command

func (*CompileInfo) GetDuration

func (m *CompileInfo) GetDuration() *google_protobuf.Duration

func (*CompileInfo) GetInputSizeBytes

func (m *CompileInfo) GetInputSizeBytes() uint64

func (*CompileInfo) GetOutputSizeBytes

func (m *CompileInfo) GetOutputSizeBytes() uint64

func (*CompileInfo) ProtoMessage

func (*CompileInfo) ProtoMessage()

func (*CompileInfo) Reset

func (m *CompileInfo) Reset()

func (*CompileInfo) String

func (m *CompileInfo) String() string

type CompileOptions

type CompileOptions struct {
	Grpc                        bool              `protobuf:"varint,1,opt,name=grpc" json:"grpc,omitempty"`
	GrpcGateway                 bool              `protobuf:"varint,2,opt,name=grpc_gateway,json=grpcGateway" json:"grpc_gateway,omitempty"`
	NoDefaultIncludes           bool              `protobuf:"varint,3,opt,name=no_default_includes,json=noDefaultIncludes" json:"no_default_includes,omitempty"`
	ExcludePattern              []string          `protobuf:"bytes,4,rep,name=exclude_pattern,json=excludePattern" json:"exclude_pattern,omitempty"`
	RelContext                  string            `protobuf:"bytes,5,opt,name=rel_context,json=relContext" json:"rel_context,omitempty"`
	ExtraPluginFlag             []string          `protobuf:"bytes,6,rep,name=extra_plugin_flag,json=extraPluginFlag" json:"extra_plugin_flag,omitempty"`
	Cpp                         bool              `protobuf:"varint,20,opt,name=cpp" json:"cpp,omitempty"`
	CppRelOut                   string            `protobuf:"bytes,21,opt,name=cpp_rel_out,json=cppRelOut" json:"cpp_rel_out,omitempty"`
	Csharp                      bool              `protobuf:"varint,30,opt,name=csharp" json:"csharp,omitempty"`
	CsharpRelOut                string            `protobuf:"bytes,31,opt,name=csharp_rel_out,json=csharpRelOut" json:"csharp_rel_out,omitempty"`
	Go                          bool              `protobuf:"varint,40,opt,name=go" json:"go,omitempty"`
	GoPluginType                GoPluginType      `protobuf:"varint,41,opt,name=go_plugin_type,json=goPluginType,enum=protoeasy.GoPluginType" json:"go_plugin_type,omitempty"`
	GoRelOut                    string            `protobuf:"bytes,42,opt,name=go_rel_out,json=goRelOut" json:"go_rel_out,omitempty"`
	GoImportPath                string            `protobuf:"bytes,43,opt,name=go_import_path,json=goImportPath" json:"go_import_path,omitempty"`
	GoNoDefaultModifiers        bool              `protobuf:"varint,44,opt,name=go_no_default_modifiers,json=goNoDefaultModifiers" json:"go_no_default_modifiers,omitempty"`
	GoModifiers                 map[string]string `` /* 162-byte string literal not displayed */
	Gogo                        bool              `protobuf:"varint,50,opt,name=gogo" json:"gogo,omitempty"`
	GogoPluginType              GogoPluginType    `` /* 130-byte string literal not displayed */
	GogoRelOut                  string            `protobuf:"bytes,52,opt,name=gogo_rel_out,json=gogoRelOut" json:"gogo_rel_out,omitempty"`
	GogoImportPath              string            `protobuf:"bytes,53,opt,name=gogo_import_path,json=gogoImportPath" json:"gogo_import_path,omitempty"`
	GogoNoDefaultModifiers      bool              `` /* 126-byte string literal not displayed */
	GogoModifiers               map[string]string `` /* 168-byte string literal not displayed */
	Objc                        bool              `protobuf:"varint,60,opt,name=objc" json:"objc,omitempty"`
	ObjcRelOut                  string            `protobuf:"bytes,61,opt,name=objc_rel_out,json=objcRelOut" json:"objc_rel_out,omitempty"`
	Python                      bool              `protobuf:"varint,70,opt,name=python" json:"python,omitempty"`
	PythonRelOut                string            `protobuf:"bytes,71,opt,name=python_rel_out,json=pythonRelOut" json:"python_rel_out,omitempty"`
	Ruby                        bool              `protobuf:"varint,80,opt,name=ruby" json:"ruby,omitempty"`
	RubyRelOut                  string            `protobuf:"bytes,81,opt,name=ruby_rel_out,json=rubyRelOut" json:"ruby_rel_out,omitempty"`
	DescriptorSet               bool              `protobuf:"varint,90,opt,name=descriptor_set,json=descriptorSet" json:"descriptor_set,omitempty"`
	DescriptorSetRelOut         string            `protobuf:"bytes,91,opt,name=descriptor_set_rel_out,json=descriptorSetRelOut" json:"descriptor_set_rel_out,omitempty"`
	DescriptorSetFileName       string            `protobuf:"bytes,92,opt,name=descriptor_set_file_name,json=descriptorSetFileName" json:"descriptor_set_file_name,omitempty"`
	DescriptorSetIncludeImports bool              `` /* 141-byte string literal not displayed */
}

func (*CompileOptions) Descriptor

func (*CompileOptions) Descriptor() ([]byte, []int)

func (*CompileOptions) GetCpp

func (m *CompileOptions) GetCpp() bool

func (*CompileOptions) GetCppRelOut

func (m *CompileOptions) GetCppRelOut() string

func (*CompileOptions) GetCsharp

func (m *CompileOptions) GetCsharp() bool

func (*CompileOptions) GetCsharpRelOut

func (m *CompileOptions) GetCsharpRelOut() string

func (*CompileOptions) GetDescriptorSet

func (m *CompileOptions) GetDescriptorSet() bool

func (*CompileOptions) GetDescriptorSetFileName

func (m *CompileOptions) GetDescriptorSetFileName() string

func (*CompileOptions) GetDescriptorSetIncludeImports

func (m *CompileOptions) GetDescriptorSetIncludeImports() bool

func (*CompileOptions) GetDescriptorSetRelOut

func (m *CompileOptions) GetDescriptorSetRelOut() string

func (*CompileOptions) GetExcludePattern

func (m *CompileOptions) GetExcludePattern() []string

func (*CompileOptions) GetExtraPluginFlag

func (m *CompileOptions) GetExtraPluginFlag() []string

func (*CompileOptions) GetGo

func (m *CompileOptions) GetGo() bool

func (*CompileOptions) GetGoImportPath

func (m *CompileOptions) GetGoImportPath() string

func (*CompileOptions) GetGoModifiers

func (m *CompileOptions) GetGoModifiers() map[string]string

func (*CompileOptions) GetGoNoDefaultModifiers

func (m *CompileOptions) GetGoNoDefaultModifiers() bool

func (*CompileOptions) GetGoPluginType

func (m *CompileOptions) GetGoPluginType() GoPluginType

func (*CompileOptions) GetGoRelOut

func (m *CompileOptions) GetGoRelOut() string

func (*CompileOptions) GetGogo

func (m *CompileOptions) GetGogo() bool

func (*CompileOptions) GetGogoImportPath

func (m *CompileOptions) GetGogoImportPath() string

func (*CompileOptions) GetGogoModifiers

func (m *CompileOptions) GetGogoModifiers() map[string]string

func (*CompileOptions) GetGogoNoDefaultModifiers

func (m *CompileOptions) GetGogoNoDefaultModifiers() bool

func (*CompileOptions) GetGogoPluginType

func (m *CompileOptions) GetGogoPluginType() GogoPluginType

func (*CompileOptions) GetGogoRelOut

func (m *CompileOptions) GetGogoRelOut() string

func (*CompileOptions) GetGrpc

func (m *CompileOptions) GetGrpc() bool

func (*CompileOptions) GetGrpcGateway

func (m *CompileOptions) GetGrpcGateway() bool

func (*CompileOptions) GetNoDefaultIncludes

func (m *CompileOptions) GetNoDefaultIncludes() bool

func (*CompileOptions) GetObjc

func (m *CompileOptions) GetObjc() bool

func (*CompileOptions) GetObjcRelOut

func (m *CompileOptions) GetObjcRelOut() string

func (*CompileOptions) GetPython

func (m *CompileOptions) GetPython() bool

func (*CompileOptions) GetPythonRelOut

func (m *CompileOptions) GetPythonRelOut() string

func (*CompileOptions) GetRelContext

func (m *CompileOptions) GetRelContext() string

func (*CompileOptions) GetRuby

func (m *CompileOptions) GetRuby() bool

func (*CompileOptions) GetRubyRelOut

func (m *CompileOptions) GetRubyRelOut() string

func (*CompileOptions) ProtoMessage

func (*CompileOptions) ProtoMessage()

func (*CompileOptions) Reset

func (m *CompileOptions) Reset()

func (*CompileOptions) String

func (m *CompileOptions) String() string

type CompileRequest

type CompileRequest struct {
	Tar            []byte          `protobuf:"bytes,1,opt,name=tar,proto3" json:"tar,omitempty"`
	CompileOptions *CompileOptions `protobuf:"bytes,2,opt,name=compile_options,json=compileOptions" json:"compile_options,omitempty"`
}

func (*CompileRequest) Descriptor

func (*CompileRequest) Descriptor() ([]byte, []int)

func (*CompileRequest) GetCompileOptions

func (m *CompileRequest) GetCompileOptions() *CompileOptions

func (*CompileRequest) GetTar

func (m *CompileRequest) GetTar() []byte

func (*CompileRequest) ProtoMessage

func (*CompileRequest) ProtoMessage()

func (*CompileRequest) Reset

func (m *CompileRequest) Reset()

func (*CompileRequest) String

func (m *CompileRequest) String() string

type CompileResponse

type CompileResponse struct {
	Tar         []byte       `protobuf:"bytes,1,opt,name=tar,proto3" json:"tar,omitempty"`
	CompileInfo *CompileInfo `protobuf:"bytes,2,opt,name=compile_info,json=compileInfo" json:"compile_info,omitempty"`
}

func (*CompileResponse) Descriptor

func (*CompileResponse) Descriptor() ([]byte, []int)

func (*CompileResponse) GetCompileInfo

func (m *CompileResponse) GetCompileInfo() *CompileInfo

func (*CompileResponse) GetTar

func (m *CompileResponse) GetTar() []byte

func (*CompileResponse) ProtoMessage

func (*CompileResponse) ProtoMessage()

func (*CompileResponse) Reset

func (m *CompileResponse) Reset()

func (*CompileResponse) String

func (m *CompileResponse) String() string

type Compiler

type Compiler interface {
	// Compile compiles the protocol buffer files in dirPath and outputs the generated
	// files to outDirPath, using the given CompileOptions.
	Compile(dirPath string, outDirPath string, compileOptions *CompileOptions) ([]*Command, error)
}

Compiler compiles protocol buffer files.

func NewClientCompiler

func NewClientCompiler(apiClient APIClient, options CompilerOptions) Compiler

NewClientCompiler returns a new client Compiler for the given APIClient.

func NewServerCompiler

func NewServerCompiler(options CompilerOptions) Compiler

NewServerCompiler returns a new server Compiler.

type CompilerOptions

type CompilerOptions struct{}

CompilerOptions are options for a Compiler.

type FileCompileOptions

type FileCompileOptions struct {
	Version string   `json:"version,omitempty" yaml:"version,omitempty"`
	Dir     string   `json:"dir,omitempty" yaml:"dir,omitempty"`
	Plugins []string `json:"plugins,omitempty" yaml:"plugins,omitempty"`
	Options struct {
		NoDefaultIncludes bool     `json:"no_default_includes,omitempty" yaml:"no_default_includes,omitempty"`
		Exclude           []string `json:"exclude,omitempty" yaml:"exclude,omitempty"`
		RelContext        string   `json:"rel_context,omitempty" yaml:"rel_context,omitempty"`
		Grpc              struct{} `json:"grpc,omitempty" yaml:"grpc,omitempty"`
		GrpcGateway       struct{} `json:"grpc_gateway,omitempty" yaml:"grpc_gateway,omitempty"`
		Cpp               struct {
			RelOut string `json:"rel_out,omitempty" yaml:"rel_out,omitempty"`
		} `json:"cpp,omitempty" yaml:"cpp,omitempty"`
		Csharp struct {
			RelOut string `json:"rel_out,omitempty" yaml:"rel_out,omitempty"`
		} `json:"csharp,omitempty" yaml:"csharp,omitempty"`
		Go struct {
			RelOut             string            `json:"rel_out,omitempty" yaml:"rel_out,omitempty"`
			PluginType         string            `json:"plugin_type,omitempty" yaml:"plugin_type,omitempty"`
			ImportPath         string            `json:"import_path,omitempty" yaml:"import_path,omitempty"`
			NoDefaultModifiers bool              `json:"no_default_modifiers,omitempty" yaml:"no_default_modifiers,omitempty"`
			Modifiers          map[string]string `json:"modifiers,omitempty" yaml:"modifiers,omitempty"`
		} `json:"go,omitempty" yaml:"go,omitempty"`
		Gogo struct {
			RelOut             string            `json:"rel_out,omitempty" yaml:"rel_out,omitempty"`
			PluginType         string            `json:"plugin_type,omitempty" yaml:"plugin_type,omitempty"`
			ImportPath         string            `json:"import_path,omitempty" yaml:"import_path,omitempty"`
			NoDefaultModifiers bool              `json:"no_default_modifiers,omitempty" yaml:"no_default_modifiers,omitempty"`
			Modifiers          map[string]string `json:"modifiers,omitempty" yaml:"modifiers,omitempty"`
		} `json:"gogo,omitempty" yaml:"gogo,omitempty"`
		Objc struct {
			RelOut string `json:"rel_out,omitempty" yaml:"rel_out,omitempty"`
		} `json:"objc,omitempty" yaml:"objc,omitempty"`
		Python struct {
			RelOut string `json:"rel_out,omitempty" yaml:"rel_out,omitempty"`
		} `json:"python,omitempty" yaml:"python,omitempty"`
		Ruby struct {
			RelOut string `json:"rel_out,omitempty" yaml:"rel_out,omitempty"`
		} `json:"ruby,omitempty" yaml:"ruby,omitempty"`
		DescriptorSet struct {
			RelOut         string `json:"rel_out,omitempty" yaml:"rel_out,omitempty"`
			FileName       string `json:"file_name,omitempty" yaml:"file_name,omitempty"`
			IncludeImports bool   `json:"include_imports,omitempty" yaml:"include_imports,omitempty"`
		} `json:"descriptor_set,omitempty" yaml:"descriptor_set,omitempty"`
	} `json:"options,omitempty" yaml:"options,omitempty"`
}

FileCompileOptions are CompileOptions with JSON and YAML tags ready to be read from a file.

func ParseFileCompileOptions

func ParseFileCompileOptions(filePath string) (*FileCompileOptions, error)

ParseFileCompileOptions parses a FileCompileOptions struct from the filePath.

If file extension is .json, JSON parsing is attempted. If file extension is .yml or .yaml, YAML parsing is attemped. Otherwise, YAML parsing is first attempted, then JSON parsing.

func (*FileCompileOptions) ToCompileOptions

func (f *FileCompileOptions) ToCompileOptions() (*CompileOptions, error)

ToCompileOptions converts a FileCompileOptions to a CompileOptions.

type GoPluginType

type GoPluginType int32
const (
	GoPluginType_GO_PLUGIN_TYPE_NONE   GoPluginType = 0
	GoPluginType_GO_PLUGIN_TYPE_GO     GoPluginType = 1
	GoPluginType_GO_PLUGIN_TYPE_GOFAST GoPluginType = 2
)

func GoPluginTypeSimpleValueOf

func GoPluginTypeSimpleValueOf(s string) (GoPluginType, error)

GoPluginTypeSimpleValueOf returns the GoPluginType for the simple value.

func (GoPluginType) EnumDescriptor

func (GoPluginType) EnumDescriptor() ([]byte, []int)

func (GoPluginType) SimpleString

func (x GoPluginType) SimpleString() string

SimpleString returns the simple value for the GoPluginType.

func (GoPluginType) String

func (x GoPluginType) String() string

type GogoPluginType

type GogoPluginType int32
const (
	GogoPluginType_GOGO_PLUGIN_TYPE_NONE       GogoPluginType = 0
	GogoPluginType_GOGO_PLUGIN_TYPE_GOGO       GogoPluginType = 1
	GogoPluginType_GOGO_PLUGIN_TYPE_GOGOFAST   GogoPluginType = 2
	GogoPluginType_GOGO_PLUGIN_TYPE_GOGOFASTER GogoPluginType = 3
	GogoPluginType_GOGO_PLUGIN_TYPE_GOGOSLICK  GogoPluginType = 4
)

func GogoPluginTypeSimpleValueOf

func GogoPluginTypeSimpleValueOf(s string) (GogoPluginType, error)

GogoPluginTypeSimpleValueOf returns the GogoPluginType for the simple value.

func (GogoPluginType) EnumDescriptor

func (GogoPluginType) EnumDescriptor() ([]byte, []int)

func (GogoPluginType) SimpleString

func (x GogoPluginType) SimpleString() string

SimpleString returns the simple value for the GogoPluginType.

func (GogoPluginType) String

func (x GogoPluginType) String() string

Directories

Path Synopsis
cmd

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL