storage: Add storage grpc wrapper

Implements the two storage services described in the proto for a grpc
server.

Change-Id: I9e1226d90d19d323736ec0cb6a3f1b70eb516a36
Reviewed-on: https://code-review.googlesource.com/c/gocloud/+/43130
Reviewed-by: kokoro <noreply+kokoro@google.com>
Reviewed-by: Jean de Klerk <deklerk@google.com>
diff --git a/storage/internal/benchwrapper/certificate/server.key b/storage/internal/benchwrapper/certificate/server.key
new file mode 100644
index 0000000..144ee0a
--- /dev/null
+++ b/storage/internal/benchwrapper/certificate/server.key
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpQIBAAKCAQEApJXna2sjwf0AV9N6rVDpWnZ/ZWsxcs4j8flrn6CwLfIURG6E
+ctQQkFCxQZWZ+M6EESzd1U/wSCRXB6Gv7tJMASlfiswy/eFcm8rX88/c94puZlll
+Hzh82dJMW2baMzzKQUobJxJP3f4V7p7qptczr0jFlAQro8kIMx7m/as/9574e3Fr
+qIk03QPapZvTp5zz3PtL7nFqsNcwVRntZ2P/AC94ypCV9jxNY9n6nPNp9pbvnGLx
+aPmBT8iUP3RZrXjzENB9k5PwR/EHn9jQL97pc4ZNFBTGPceWHl76vVYDI4Har4GA
+voWEWfEBGpwUQ/ArX1J/32avLP4ciI5B3GTxtQIDAQABAoIBAQCbs1CVt2rBhwY/
+D8q7kZLgyL9mg4zpDgjFTm85OPvSy3PO9TqJowL0c9HsuIlL7prT4L0PoqsupGpM
+xvI4HWxXoPtIxDrtILjKjY/UkG17JNOiManPalH6gs9HnLqJxFWWhHPBd7pkyY2Y
+C3DzWx0DEvYPPA7IyFLggbiGe+5s4stMRv8P8kF2RN5J1UIL7LNJzNpnd4rY28q1
+LsHDbnNGvGu4uHRRFsluYSQSSMC0A7ZHNFvM0K6HqZjublBh23trpgOdahOoPmDq
+6WF7TlsKhYgol5mhXLehWn2KtJU71VNUJUKwWVnXVXH5ZgZWDFJpoJJJ121jsngC
+4N3BFEbBAoGBANgtZNFm8asUvTBQiC93dQBzB/zvrcNIFXhA5TFnCo9XWPCEBUlV
+znNTVifTyMJe9dMZ+N2jG0te0lpnPZ6lg2XwvAxIJYeJe0MAWgnUvJDtFTH+gasO
+PvEk5ABmaVKb9mSSamG3ameK5H7t4GmGOHFdg0RYLM7rxRigJpJYkculAoGBAMLn
+hLM7vmuVhRDt5YQEfewDMF97OUppd+nH73xI0ooq5cqlZ8Oe9W7JJB8T0TwlVprN
+aaXVvxzwKhSy9+LwKNzIiXsUL2pS+J9m8itMF6/p9o9BhpgXUK7LEZ25GgThkKjE
+eSnIEzZ48Y59BO9N8cNntRy5M6TsWYRATTlGN/DRAoGBAM8AMLD/dVxP4JU6GB3z
+slq6y6RdUbNT/BMJeS4kMA1wI58zm+hMPQNbgn1io7PTwGrjTNXWkJVMd/rPL0cR
+0E+2BCtyjlVJxXk0BgOx7NqN2yTjQwEsBANfQuBLMIFFDNvjA/EL89ywN/p/B8uk
+FbogREkWcdm2FCZvfF/YZcttAoGBAJTxG6VkSHYqgs+XHMewLxJGtZahsfw9cNeE
+ZGzTsaDIe2ZK1GrXdLM+no3pNQIDfpU52DxUhYF1vpKm2WvhHzjpPraBjGJyGXCm
+2PVhZtCVuEdtSY+8cXnB0Sp4o3uLbGKsEfNt49DCx8WdoEHHjLNI80yJ8v9bZmT8
+Roc4M9OhAoGAKNyqyXuKH5jPjklYQieC21b2AoE7P/vnW28YXkKw6QP9Mq9zuDAt
+U8dmtsNCLnPjcU4DMeAF50Nv6jla2xI07G9t3dNqH164T+2mvESL8g10mfpWcC9E
+EXDINK0yHq8Yxxzce/IlEUdBhwc331MEMVye09XxiF2tla5Agl0HFSc=
+-----END RSA PRIVATE KEY-----
diff --git a/storage/internal/benchwrapper/certificate/server.pem b/storage/internal/benchwrapper/certificate/server.pem
new file mode 100644
index 0000000..a90b80b
--- /dev/null
+++ b/storage/internal/benchwrapper/certificate/server.pem
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDIzCCAgugAwIBAgIUcjEcAhzPqcDU5qIqpggmZtCbyc4wDQYJKoZIhvcNAQEL
+BQAwITELMAkGA1UEBhMCVVMxEjAQBgNVBAMMCWxvY2FsaG9zdDAeFw0xOTA3MzEy
+MDMwMDNaFw0xOTA4MzAyMDMwMDNaMCExCzAJBgNVBAYTAlVTMRIwEAYDVQQDDAls
+b2NhbGhvc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCkledrayPB
+/QBX03qtUOladn9lazFyziPx+WufoLAt8hREboRy1BCQULFBlZn4zoQRLN3VT/BI
+JFcHoa/u0kwBKV+KzDL94Vybytfzz9z3im5mWWUfOHzZ0kxbZtozPMpBShsnEk/d
+/hXunuqm1zOvSMWUBCujyQgzHub9qz/3nvh7cWuoiTTdA9qlm9OnnPPc+0vucWqw
+1zBVGe1nY/8AL3jKkJX2PE1j2fqc82n2lu+cYvFo+YFPyJQ/dFmtePMQ0H2Tk/BH
+8Qef2NAv3ulzhk0UFMY9x5YeXvq9VgMjgdqvgYC+hYRZ8QEanBRD8CtfUn/fZq8s
+/hyIjkHcZPG1AgMBAAGjUzBRMB0GA1UdDgQWBBQCklzwk3jc7ykJKL1ELG6Ol9t0
+HTAfBgNVHSMEGDAWgBQCklzwk3jc7ykJKL1ELG6Ol9t0HTAPBgNVHRMBAf8EBTAD
+AQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAXbMfhj0N7+JvVQ79PIW1rnAAAGTkoT42N
+uDeIUXDAixRIurmI2gMhefvvwfj65+coaAMs6rHz9wBq0L/RWAH2XvmpfG5asnAM
+yJqgwdJQysUBy4NxPDwbo9mLc0eZ2xoh0l2KYASCVfYsvdYD/1MTouuBVj56RzNC
+1IyT1yWxCTrobFmOqCSjKcQS2Vm+Mv/ui4oxs9mJZswz2K8CxNII9jYBcbpB8LGz
+jEZkqt7Od1hLSRHD9+K7/j1T9l+Qk2Uuo24B7baHDeBmmFWuj3k2ddXPj/h2NbbQ
+2Mso3N//8UfGdvqtGQ1U3Dtxa0ZbhsAsxQrBcwnX7xe3fi3EGS+G
+-----END CERTIFICATE-----
diff --git a/storage/internal/benchwrapper/main.go b/storage/internal/benchwrapper/main.go
new file mode 100644
index 0000000..f3ff33d
--- /dev/null
+++ b/storage/internal/benchwrapper/main.go
@@ -0,0 +1,113 @@
+// Copyright 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Package main wraps the client library in a gRPC interface that a benchmarker can communicate through.
+package main
+
+import (
+	"context"
+	"io"
+	"io/ioutil"
+	"log"
+	"net"
+	"path/filepath"
+
+	"cloud.google.com/go/storage"
+	pb "cloud.google.com/go/storage/internal/benchwrapper/proto"
+	"google.golang.org/grpc"
+	"google.golang.org/grpc/credentials"
+)
+
+const (
+	// Ephemeral port to run grpc service.
+	port = ":50051"
+	// minRead respresents the number of bytes to read at a time.
+	minRead = 4
+)
+
+func main() {
+	ctx := context.Background()
+	c, err := storage.NewClient(ctx)
+	if err != nil {
+		log.Fatal(err)
+	}
+	lis, err := net.Listen("tcp", port)
+	if err != nil {
+		log.Fatal(err)
+	}
+	certificate, err := filepath.Abs("benchwrapper/certificate/server.pem")
+	if err != nil {
+		log.Fatal(err)
+	}
+	key, err := filepath.Abs("benchwrapper/certificate/server.key")
+	if err != nil {
+		log.Fatal(err)
+	}
+	creds, err := credentials.NewServerTLSFromFile(certificate, key)
+	if err != nil {
+		log.Fatal(err)
+	}
+	s := grpc.NewServer(grpc.Creds(creds))
+	pb.RegisterStorageBenchWrapperServer(s, &server{
+		c: c,
+	})
+	log.Printf("Running on %s\n", port)
+	log.Fatal(s.Serve(lis))
+}
+
+type server struct {
+	c *storage.Client
+}
+
+func (s *server) Read(ctx context.Context, in *pb.ObjectRead) (*pb.EmptyResponse, error) {
+	b := s.c.Bucket(in.GetBucketName())
+	o := b.Object(in.GetObjectName())
+	r, err := o.NewReader(ctx)
+	if err != nil {
+		return nil, err
+	}
+	defer r.Close()
+	for int(r.Remain()) > 0 {
+		ba := make([]byte, minRead)
+		_, err := r.Read(ba)
+		if err == io.EOF {
+			return &pb.EmptyResponse{}, nil
+		}
+		if err != nil {
+			return nil, err
+		}
+	}
+	return &pb.EmptyResponse{}, nil
+}
+
+func (s *server) Write(ctx context.Context, in *pb.ObjectWrite) (*pb.EmptyResponse, error) {
+	b := s.c.Bucket(in.GetBucketName())
+	o := b.Object(in.GetObjectName())
+	w := o.NewWriter(ctx)
+	content, err := ioutil.ReadFile(in.GetDestination())
+	if err != nil {
+		return nil, err
+	}
+	w.ContentType = "text/plain"
+	if _, err := w.Write([]byte(content)); err != nil {
+		return nil, err
+	}
+	if err := w.Close(); err != nil {
+		if err == io.EOF {
+			return &pb.EmptyResponse{}, nil
+		}
+		return nil, err
+	}
+	return &pb.EmptyResponse{}, nil
+}
diff --git a/storage/internal/benchwrapper/proto/storage.pb.go b/storage/internal/benchwrapper/proto/storage.pb.go
new file mode 100644
index 0000000..c4d55a3
--- /dev/null
+++ b/storage/internal/benchwrapper/proto/storage.pb.go
@@ -0,0 +1,296 @@
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// source: storage.proto
+
+package storage_bench
+
+import (
+	context "context"
+	fmt "fmt"
+	math "math"
+
+	proto "github.com/golang/protobuf/proto"
+	grpc "google.golang.org/grpc"
+)
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ = proto.Marshal
+var _ = fmt.Errorf
+var _ = math.Inf
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the proto package it is being compiled against.
+// A compilation error at this line likely means your copy of the
+// proto package needs to be updated.
+const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
+
+type ObjectRead struct {
+	// The bucket string identifier.
+	BucketName string `protobuf:"bytes,1,opt,name=bucketName,proto3" json:"bucketName,omitempty"`
+	// The object/blob string identifier.
+	ObjectName           string   `protobuf:"bytes,2,opt,name=objectName,proto3" json:"objectName,omitempty"`
+	XXX_NoUnkeyedLiteral struct{} `json:"-"`
+	XXX_unrecognized     []byte   `json:"-"`
+	XXX_sizecache        int32    `json:"-"`
+}
+
+func (m *ObjectRead) Reset()         { *m = ObjectRead{} }
+func (m *ObjectRead) String() string { return proto.CompactTextString(m) }
+func (*ObjectRead) ProtoMessage()    {}
+func (*ObjectRead) Descriptor() ([]byte, []int) {
+	return fileDescriptor_0d2c4ccf1453ffdb, []int{0}
+}
+
+func (m *ObjectRead) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_ObjectRead.Unmarshal(m, b)
+}
+func (m *ObjectRead) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_ObjectRead.Marshal(b, m, deterministic)
+}
+func (m *ObjectRead) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_ObjectRead.Merge(m, src)
+}
+func (m *ObjectRead) XXX_Size() int {
+	return xxx_messageInfo_ObjectRead.Size(m)
+}
+func (m *ObjectRead) XXX_DiscardUnknown() {
+	xxx_messageInfo_ObjectRead.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_ObjectRead proto.InternalMessageInfo
+
+func (m *ObjectRead) GetBucketName() string {
+	if m != nil {
+		return m.BucketName
+	}
+	return ""
+}
+
+func (m *ObjectRead) GetObjectName() string {
+	if m != nil {
+		return m.ObjectName
+	}
+	return ""
+}
+
+type ObjectWrite struct {
+	// The bucket string identifier.
+	BucketName string `protobuf:"bytes,1,opt,name=bucketName,proto3" json:"bucketName,omitempty"`
+	// The object/blob string identifiers.
+	ObjectName string `protobuf:"bytes,2,opt,name=objectName,proto3" json:"objectName,omitempty"`
+	// The string containing the destination path.
+	Destination          string   `protobuf:"bytes,3,opt,name=destination,proto3" json:"destination,omitempty"`
+	XXX_NoUnkeyedLiteral struct{} `json:"-"`
+	XXX_unrecognized     []byte   `json:"-"`
+	XXX_sizecache        int32    `json:"-"`
+}
+
+func (m *ObjectWrite) Reset()         { *m = ObjectWrite{} }
+func (m *ObjectWrite) String() string { return proto.CompactTextString(m) }
+func (*ObjectWrite) ProtoMessage()    {}
+func (*ObjectWrite) Descriptor() ([]byte, []int) {
+	return fileDescriptor_0d2c4ccf1453ffdb, []int{1}
+}
+
+func (m *ObjectWrite) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_ObjectWrite.Unmarshal(m, b)
+}
+func (m *ObjectWrite) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_ObjectWrite.Marshal(b, m, deterministic)
+}
+func (m *ObjectWrite) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_ObjectWrite.Merge(m, src)
+}
+func (m *ObjectWrite) XXX_Size() int {
+	return xxx_messageInfo_ObjectWrite.Size(m)
+}
+func (m *ObjectWrite) XXX_DiscardUnknown() {
+	xxx_messageInfo_ObjectWrite.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_ObjectWrite proto.InternalMessageInfo
+
+func (m *ObjectWrite) GetBucketName() string {
+	if m != nil {
+		return m.BucketName
+	}
+	return ""
+}
+
+func (m *ObjectWrite) GetObjectName() string {
+	if m != nil {
+		return m.ObjectName
+	}
+	return ""
+}
+
+func (m *ObjectWrite) GetDestination() string {
+	if m != nil {
+		return m.Destination
+	}
+	return ""
+}
+
+type EmptyResponse struct {
+	XXX_NoUnkeyedLiteral struct{} `json:"-"`
+	XXX_unrecognized     []byte   `json:"-"`
+	XXX_sizecache        int32    `json:"-"`
+}
+
+func (m *EmptyResponse) Reset()         { *m = EmptyResponse{} }
+func (m *EmptyResponse) String() string { return proto.CompactTextString(m) }
+func (*EmptyResponse) ProtoMessage()    {}
+func (*EmptyResponse) Descriptor() ([]byte, []int) {
+	return fileDescriptor_0d2c4ccf1453ffdb, []int{2}
+}
+
+func (m *EmptyResponse) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_EmptyResponse.Unmarshal(m, b)
+}
+func (m *EmptyResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_EmptyResponse.Marshal(b, m, deterministic)
+}
+func (m *EmptyResponse) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_EmptyResponse.Merge(m, src)
+}
+func (m *EmptyResponse) XXX_Size() int {
+	return xxx_messageInfo_EmptyResponse.Size(m)
+}
+func (m *EmptyResponse) XXX_DiscardUnknown() {
+	xxx_messageInfo_EmptyResponse.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_EmptyResponse proto.InternalMessageInfo
+
+func init() {
+	proto.RegisterType((*ObjectRead)(nil), "storage_bench.ObjectRead")
+	proto.RegisterType((*ObjectWrite)(nil), "storage_bench.ObjectWrite")
+	proto.RegisterType((*EmptyResponse)(nil), "storage_bench.EmptyResponse")
+}
+
+func init() { proto.RegisterFile("storage.proto", fileDescriptor_0d2c4ccf1453ffdb) }
+
+var fileDescriptor_0d2c4ccf1453ffdb = []byte{
+	// 208 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0x2d, 0x2e, 0xc9, 0x2f,
+	0x4a, 0x4c, 0x4f, 0xd5, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x82, 0x71, 0xe3, 0x93, 0x52, 0xf3,
+	0x92, 0x33, 0x94, 0x7c, 0xb8, 0xb8, 0xfc, 0x93, 0xb2, 0x52, 0x93, 0x4b, 0x82, 0x52, 0x13, 0x53,
+	0x84, 0xe4, 0xb8, 0xb8, 0x92, 0x4a, 0x93, 0xb3, 0x53, 0x4b, 0xfc, 0x12, 0x73, 0x53, 0x25, 0x18,
+	0x15, 0x18, 0x35, 0x38, 0x83, 0x90, 0x44, 0x40, 0xf2, 0xf9, 0x60, 0xd5, 0x60, 0x79, 0x26, 0x88,
+	0x3c, 0x42, 0x44, 0x29, 0x9f, 0x8b, 0x1b, 0x62, 0x5a, 0x78, 0x51, 0x66, 0x49, 0x2a, 0xa5, 0xc6,
+	0x09, 0x29, 0x70, 0x71, 0xa7, 0xa4, 0x16, 0x97, 0x64, 0xe6, 0x25, 0x96, 0x64, 0xe6, 0xe7, 0x49,
+	0x30, 0x83, 0x15, 0x20, 0x0b, 0x29, 0xf1, 0x73, 0xf1, 0xba, 0xe6, 0x16, 0x94, 0x54, 0x06, 0xa5,
+	0x16, 0x17, 0xe4, 0xe7, 0x15, 0xa7, 0x1a, 0xcd, 0x65, 0xe4, 0x12, 0x0e, 0x86, 0xf8, 0xd0, 0x09,
+	0xe4, 0xc1, 0xf0, 0xa2, 0xc4, 0x82, 0x82, 0xd4, 0x22, 0x21, 0x67, 0x2e, 0x56, 0x88, 0x9b, 0xa4,
+	0xf4, 0x50, 0x02, 0x40, 0x0f, 0xc9, 0xbd, 0x52, 0x32, 0x68, 0x72, 0x28, 0x46, 0x2b, 0x31, 0x08,
+	0x39, 0x72, 0xb1, 0x80, 0x83, 0x49, 0x12, 0xab, 0x19, 0x20, 0x29, 0x42, 0x46, 0x24, 0xb1, 0x81,
+	0x63, 0xc1, 0x18, 0x10, 0x00, 0x00, 0xff, 0xff, 0xb3, 0xe8, 0x1e, 0xa7, 0x96, 0x01, 0x00, 0x00,
+}
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ context.Context
+var _ grpc.ClientConn
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the grpc package it is being compiled against.
+const _ = grpc.SupportPackageIsVersion4
+
+// StorageBenchWrapperClient is the client API for StorageBenchWrapper service.
+//
+// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
+type StorageBenchWrapperClient interface {
+	// Performs an upload from a specific object.
+	Write(ctx context.Context, in *ObjectWrite, opts ...grpc.CallOption) (*EmptyResponse, error)
+	// Read a specific object.
+	Read(ctx context.Context, in *ObjectRead, opts ...grpc.CallOption) (*EmptyResponse, error)
+}
+
+type storageBenchWrapperClient struct {
+	cc *grpc.ClientConn
+}
+
+func NewStorageBenchWrapperClient(cc *grpc.ClientConn) StorageBenchWrapperClient {
+	return &storageBenchWrapperClient{cc}
+}
+
+func (c *storageBenchWrapperClient) Write(ctx context.Context, in *ObjectWrite, opts ...grpc.CallOption) (*EmptyResponse, error) {
+	out := new(EmptyResponse)
+	err := c.cc.Invoke(ctx, "/storage_bench.StorageBenchWrapper/Write", in, out, opts...)
+	if err != nil {
+		return nil, err
+	}
+	return out, nil
+}
+
+func (c *storageBenchWrapperClient) Read(ctx context.Context, in *ObjectRead, opts ...grpc.CallOption) (*EmptyResponse, error) {
+	out := new(EmptyResponse)
+	err := c.cc.Invoke(ctx, "/storage_bench.StorageBenchWrapper/Read", in, out, opts...)
+	if err != nil {
+		return nil, err
+	}
+	return out, nil
+}
+
+// StorageBenchWrapperServer is the server API for StorageBenchWrapper service.
+type StorageBenchWrapperServer interface {
+	// Performs an upload from a specific object.
+	Write(context.Context, *ObjectWrite) (*EmptyResponse, error)
+	// Read a specific object.
+	Read(context.Context, *ObjectRead) (*EmptyResponse, error)
+}
+
+func RegisterStorageBenchWrapperServer(s *grpc.Server, srv StorageBenchWrapperServer) {
+	s.RegisterService(&_StorageBenchWrapper_serviceDesc, srv)
+}
+
+func _StorageBenchWrapper_Write_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+	in := new(ObjectWrite)
+	if err := dec(in); err != nil {
+		return nil, err
+	}
+	if interceptor == nil {
+		return srv.(StorageBenchWrapperServer).Write(ctx, in)
+	}
+	info := &grpc.UnaryServerInfo{
+		Server:     srv,
+		FullMethod: "/storage_bench.StorageBenchWrapper/Write",
+	}
+	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+		return srv.(StorageBenchWrapperServer).Write(ctx, req.(*ObjectWrite))
+	}
+	return interceptor(ctx, in, info, handler)
+}
+
+func _StorageBenchWrapper_Read_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+	in := new(ObjectRead)
+	if err := dec(in); err != nil {
+		return nil, err
+	}
+	if interceptor == nil {
+		return srv.(StorageBenchWrapperServer).Read(ctx, in)
+	}
+	info := &grpc.UnaryServerInfo{
+		Server:     srv,
+		FullMethod: "/storage_bench.StorageBenchWrapper/Read",
+	}
+	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+		return srv.(StorageBenchWrapperServer).Read(ctx, req.(*ObjectRead))
+	}
+	return interceptor(ctx, in, info, handler)
+}
+
+var _StorageBenchWrapper_serviceDesc = grpc.ServiceDesc{
+	ServiceName: "storage_bench.StorageBenchWrapper",
+	HandlerType: (*StorageBenchWrapperServer)(nil),
+	Methods: []grpc.MethodDesc{
+		{
+			MethodName: "Write",
+			Handler:    _StorageBenchWrapper_Write_Handler,
+		},
+		{
+			MethodName: "Read",
+			Handler:    _StorageBenchWrapper_Read_Handler,
+		},
+	},
+	Streams:  []grpc.StreamDesc{},
+	Metadata: "storage.proto",
+}
diff --git a/storage/internal/benchwrapper/proto/storage.proto b/storage/internal/benchwrapper/proto/storage.proto
new file mode 100644
index 0000000..cc435f3
--- /dev/null
+++ b/storage/internal/benchwrapper/proto/storage.proto
@@ -0,0 +1,43 @@
+// Copyright 2016 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+syntax = "proto3";
+
+package storage_bench;
+
+message ObjectRead{
+  // The bucket string identifier.
+  string bucketName = 1;
+  // The object/blob string identifier.
+  string objectName = 2;
+}
+
+message ObjectWrite{
+   // The bucket string identifier.
+  string bucketName = 1;
+  // The object/blob string identifiers.
+  string objectName = 2;
+  // The string containing the upload file path.
+  string destination = 3;
+}
+
+message EmptyResponse{
+}
+
+service StorageBenchWrapper{
+  // Performs an upload from a specific object.
+  rpc Write(ObjectWrite) returns (EmptyResponse) {}
+  // Read a specific object.
+  rpc Read(ObjectRead) returns (EmptyResponse){}
+}