internal/gomote, internal/gomote/protos: add ReadTGZToURL endpoint

This change adds the ReadTGZToURL endpoint implementation. This tars and
zips the directory requested on the gomote instance and uploads it to
GCS. A signed URL is returned which the caller can use to download the file.

For golang/go#47521
Updates golang/go#48742

Change-Id: I5e9574994810b804acb4b9ed9e6bdda68ea26713
Reviewed-on: https://go-review.googlesource.com/c/build/+/397598
Run-TryBot: Carlos Amedee <carlos@golang.org>
Auto-Submit: Carlos Amedee <carlos@golang.org>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Carlos Amedee <carlos@golang.org>
Reviewed-by: Heschi Kreinick <heschi@google.com>
diff --git a/buildlet/fakebuildletclient.go b/buildlet/fakebuildletclient.go
index 70890c6..af14803 100644
--- a/buildlet/fakebuildletclient.go
+++ b/buildlet/fakebuildletclient.go
@@ -109,7 +109,8 @@
 
 // GetTar gives a vake tar zipped directory.
 func (fc *FakeClient) GetTar(ctx context.Context, dir string) (io.ReadCloser, error) {
-	return nil, errUnimplemented
+	r := strings.NewReader("the gopher goes to the sea and fights the kraken")
+	return io.NopCloser(r), nil
 }
 
 // IPPort provides a fake ip and port pair.
diff --git a/internal/gomote/gomote.go b/internal/gomote/gomote.go
index 295a769..cb830cb 100644
--- a/internal/gomote/gomote.go
+++ b/internal/gomote/gomote.go
@@ -273,11 +273,19 @@
 	remoteErr, execErr := bc.Exec(stream.Context(), req.GetCommand(), buildlet.ExecOpts{
 		Dir:         req.GetCommand(),
 		SystemLevel: req.GetSystemLevel(),
-		Output:      &execStreamWriter{stream: stream},
-		Args:        req.GetArgs(),
-		ExtraEnv:    req.GetAppendEnvironment(),
-		Debug:       req.GetDebug(),
-		Path:        req.GetPath(),
+		Output: &streamWriter{writeFunc: func(p []byte) (int, error) {
+			err := stream.Send(&protos.ExecuteCommandResponse{
+				Output: string(p),
+			})
+			if err != nil {
+				return 0, fmt.Errorf("unable to send data=%w", err)
+			}
+			return len(p), nil
+		}},
+		Args:     req.GetArgs(),
+		ExtraEnv: req.GetAppendEnvironment(),
+		Debug:    req.GetDebug(),
+		Path:     req.GetPath(),
 	})
 	if execErr != nil {
 		// there were system errors preventing the command from being started or seen to completition.
@@ -290,21 +298,55 @@
 	return nil
 }
 
-// execStreamWriter implements the io.Writer interface. Any data writen to it will be streamed
-// as an execute command response.
-type execStreamWriter struct {
-	stream protos.GomoteService_ExecuteCommandServer
+// streamWriter implements the io.Writer interface.
+type streamWriter struct {
+	writeFunc func(p []byte) (int, error)
 }
 
-// Write sends data writen to it as an execute command response.
-func (sw *execStreamWriter) Write(p []byte) (int, error) {
-	err := sw.stream.Send(&protos.ExecuteCommandResponse{
-		Output: string(p),
-	})
+// Write calls the writeFunc function with the same arguments passed to the Write function.
+func (sw *streamWriter) Write(p []byte) (int, error) {
+	return sw.writeFunc(p)
+}
+
+// ReadTGZToURL retrieves a directory from the gomote instance and writes the file to GCS. It returns a signed URL which the caller uses
+// to read the file from GCS.
+func (s *Server) ReadTGZToURL(ctx context.Context, req *protos.ReadTGZToURLRequest) (*protos.ReadTGZToURLResponse, error) {
+	creds, err := access.IAPFromContext(ctx)
 	if err != nil {
-		return 0, fmt.Errorf("unable to send data=%w", err)
+		return nil, status.Errorf(codes.Unauthenticated, "request does not contain the required authentication")
 	}
-	return len(p), nil
+	_, bc, err := s.sessionAndClient(req.GetGomoteId(), creds.ID)
+	if err != nil {
+		// the helper function returns meaningful GRPC error.
+		return nil, err
+	}
+	tgz, err := bc.GetTar(ctx, req.GetDirectory())
+	if err != nil {
+		return nil, status.Errorf(codes.Aborted, "unable to retrieve tar from gomote instance: %s", err)
+	}
+	defer tgz.Close()
+	objectName := uuid.NewString()
+	objectHandle := s.bucket.Object(objectName)
+	// A context for writes is used to ensure we can cancel the context if a
+	// problem is encountered while writing to the object store. The API documentation
+	// states that the context should be canceled to stop writing without saving the data.
+	writeCtx, cancel := context.WithCancel(ctx)
+	tgzWriter := objectHandle.NewWriter(writeCtx)
+	defer cancel()
+	if _, err = io.Copy(tgzWriter, tgz); err != nil {
+		return nil, status.Errorf(codes.Aborted, "unable to stream tar.gz: %s", err)
+	}
+	// when close is called, the object is stored in the bucket.
+	if err := tgzWriter.Close(); err != nil {
+		return nil, status.Errorf(codes.Aborted, "unable to store object: %s", err)
+	}
+	url, err := s.signURLForDownload(objectName)
+	if err != nil {
+		return nil, status.Errorf(codes.Internal, "unable to create signed URL for download: %s", err)
+	}
+	return &protos.ReadTGZToURLResponse{
+		Url: url,
+	}, nil
 }
 
 // RemoveFiles removes files or directories from the gomote instance.
diff --git a/internal/gomote/gomote_test.go b/internal/gomote/gomote_test.go
index 12ebf1c..440f623 100644
--- a/internal/gomote/gomote_test.go
+++ b/internal/gomote/gomote_test.go
@@ -522,6 +522,66 @@
 	}
 }
 
+func TestReadTGZToURLError(t *testing.T) {
+	// This test will create a gomote instance and attempt to call ReadTGZToURL.
+	// If overrideID is set to true, the test will use a different gomoteID than the
+	// the one created for the test.
+	testCases := []struct {
+		desc       string
+		ctx        context.Context
+		overrideID bool
+		gomoteID   string // Used iff overrideID is true.
+		directory  string
+		wantCode   codes.Code
+	}{
+		{
+			desc:     "unauthenticated request",
+			ctx:      context.Background(),
+			wantCode: codes.Unauthenticated,
+		},
+		{
+			desc:       "missing gomote id",
+			ctx:        access.FakeContextWithOutgoingIAPAuth(context.Background(), fakeIAP()),
+			overrideID: true,
+			gomoteID:   "",
+			wantCode:   codes.NotFound,
+		},
+		{
+			desc:       "gomote does not exist",
+			ctx:        access.FakeContextWithOutgoingIAPAuth(context.Background(), fakeIAPWithUser("foo", "bar")),
+			overrideID: true,
+			gomoteID:   "chucky",
+			wantCode:   codes.NotFound,
+		},
+		{
+			desc:       "wrong gomote id",
+			ctx:        access.FakeContextWithOutgoingIAPAuth(context.Background(), fakeIAPWithUser("foo", "bar")),
+			overrideID: false,
+			wantCode:   codes.PermissionDenied,
+		},
+	}
+	for _, tc := range testCases {
+		t.Run(tc.desc, func(t *testing.T) {
+			client := setupGomoteTest(t, context.Background())
+			gomoteID := mustCreateInstance(t, client, fakeIAP())
+			if tc.overrideID {
+				gomoteID = tc.gomoteID
+			}
+			req := &protos.ReadTGZToURLRequest{
+				GomoteId:  gomoteID,
+				Directory: tc.directory,
+			}
+			got, err := client.ReadTGZToURL(tc.ctx, req)
+			if err != nil && status.Code(err) != tc.wantCode {
+				t.Fatalf("unexpected error: %s; want %s", err, tc.wantCode)
+			}
+			if err == nil {
+				t.Fatalf("client.ReadTGZToURL(ctx, %v) = %v, nil; want error", req, got)
+			}
+		})
+	}
+}
+
 func TestRemoveFiles(t *testing.T) {
 	ctx := access.FakeContextWithOutgoingIAPAuth(context.Background(), fakeIAP())
 	client := setupGomoteTest(t, context.Background())
diff --git a/internal/gomote/protos/gomote.pb.go b/internal/gomote/protos/gomote.pb.go
index 66adbbc..d372ef6 100644
--- a/internal/gomote/protos/gomote.pb.go
+++ b/internal/gomote/protos/gomote.pb.go
@@ -909,15 +909,20 @@
 	return nil
 }
 
-// ReadTGZRequest specifies the data needed to retrieve a tar and zipped directory from a gomote instance.
-type ReadTGZRequest struct {
+// ReadTGZToURLRequest specifies the data needed to retrieve a tar and zipped directory from a gomote instance.
+type ReadTGZToURLRequest struct {
 	state         protoimpl.MessageState
 	sizeCache     protoimpl.SizeCache
 	unknownFields protoimpl.UnknownFields
+
+	// The unique identifier for a gomote instance.
+	GomoteId string `protobuf:"bytes,1,opt,name=gomote_id,json=gomoteId,proto3" json:"gomote_id,omitempty"`
+	// The relative directory from the gomote's work directory to tar up.
+	Directory string `protobuf:"bytes,2,opt,name=directory,proto3" json:"directory,omitempty"`
 }
 
-func (x *ReadTGZRequest) Reset() {
-	*x = ReadTGZRequest{}
+func (x *ReadTGZToURLRequest) Reset() {
+	*x = ReadTGZToURLRequest{}
 	if protoimpl.UnsafeEnabled {
 		mi := &file_gomote_proto_msgTypes[15]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
@@ -925,13 +930,13 @@
 	}
 }
 
-func (x *ReadTGZRequest) String() string {
+func (x *ReadTGZToURLRequest) String() string {
 	return protoimpl.X.MessageStringOf(x)
 }
 
-func (*ReadTGZRequest) ProtoMessage() {}
+func (*ReadTGZToURLRequest) ProtoMessage() {}
 
-func (x *ReadTGZRequest) ProtoReflect() protoreflect.Message {
+func (x *ReadTGZToURLRequest) ProtoReflect() protoreflect.Message {
 	mi := &file_gomote_proto_msgTypes[15]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
@@ -943,20 +948,37 @@
 	return mi.MessageOf(x)
 }
 
-// Deprecated: Use ReadTGZRequest.ProtoReflect.Descriptor instead.
-func (*ReadTGZRequest) Descriptor() ([]byte, []int) {
+// Deprecated: Use ReadTGZToURLRequest.ProtoReflect.Descriptor instead.
+func (*ReadTGZToURLRequest) Descriptor() ([]byte, []int) {
 	return file_gomote_proto_rawDescGZIP(), []int{15}
 }
 
-// ReadTGZResponse contains a tar and zipped directory from a gomote instance.
-type ReadTGZResponse struct {
+func (x *ReadTGZToURLRequest) GetGomoteId() string {
+	if x != nil {
+		return x.GomoteId
+	}
+	return ""
+}
+
+func (x *ReadTGZToURLRequest) GetDirectory() string {
+	if x != nil {
+		return x.Directory
+	}
+	return ""
+}
+
+// ReadTGZToURLResponse contains a URL where the tar and zipped directory from a gomote instance can be downloaded from.
+type ReadTGZToURLResponse struct {
 	state         protoimpl.MessageState
 	sizeCache     protoimpl.SizeCache
 	unknownFields protoimpl.UnknownFields
+
+	// URL to retrieve the tarball from.
+	Url string `protobuf:"bytes,1,opt,name=url,proto3" json:"url,omitempty"`
 }
 
-func (x *ReadTGZResponse) Reset() {
-	*x = ReadTGZResponse{}
+func (x *ReadTGZToURLResponse) Reset() {
+	*x = ReadTGZToURLResponse{}
 	if protoimpl.UnsafeEnabled {
 		mi := &file_gomote_proto_msgTypes[16]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
@@ -964,13 +986,13 @@
 	}
 }
 
-func (x *ReadTGZResponse) String() string {
+func (x *ReadTGZToURLResponse) String() string {
 	return protoimpl.X.MessageStringOf(x)
 }
 
-func (*ReadTGZResponse) ProtoMessage() {}
+func (*ReadTGZToURLResponse) ProtoMessage() {}
 
-func (x *ReadTGZResponse) ProtoReflect() protoreflect.Message {
+func (x *ReadTGZToURLResponse) ProtoReflect() protoreflect.Message {
 	mi := &file_gomote_proto_msgTypes[16]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
@@ -982,11 +1004,18 @@
 	return mi.MessageOf(x)
 }
 
-// Deprecated: Use ReadTGZResponse.ProtoReflect.Descriptor instead.
-func (*ReadTGZResponse) Descriptor() ([]byte, []int) {
+// Deprecated: Use ReadTGZToURLResponse.ProtoReflect.Descriptor instead.
+func (*ReadTGZToURLResponse) Descriptor() ([]byte, []int) {
 	return file_gomote_proto_rawDescGZIP(), []int{16}
 }
 
+func (x *ReadTGZToURLResponse) GetUrl() string {
+	if x != nil {
+		return x.Url
+	}
+	return ""
+}
+
 // RemoveFilesRequest specifies the data needed to remove files or directories from a gomote instance.
 type RemoveFilesRequest struct {
 	state         protoimpl.MessageState
@@ -1596,123 +1625,129 @@
 	0x73, 0x65, 0x12, 0x2e, 0x0a, 0x09, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x73, 0x18,
 	0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x49,
 	0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x52, 0x09, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63,
-	0x65, 0x73, 0x22, 0x10, 0x0a, 0x0e, 0x52, 0x65, 0x61, 0x64, 0x54, 0x47, 0x5a, 0x52, 0x65, 0x71,
-	0x75, 0x65, 0x73, 0x74, 0x22, 0x11, 0x0a, 0x0f, 0x52, 0x65, 0x61, 0x64, 0x54, 0x47, 0x5a, 0x52,
-	0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x47, 0x0a, 0x12, 0x52, 0x65, 0x6d, 0x6f, 0x76,
-	0x65, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a,
-	0x09, 0x67, 0x6f, 0x6d, 0x6f, 0x74, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
-	0x52, 0x08, 0x67, 0x6f, 0x6d, 0x6f, 0x74, 0x65, 0x49, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x70, 0x61,
-	0x74, 0x68, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x70, 0x61, 0x74, 0x68, 0x73,
-	0x22, 0x15, 0x0a, 0x13, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x52,
-	0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x56, 0x0a, 0x11, 0x53, 0x69, 0x67, 0x6e, 0x53,
-	0x53, 0x48, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09,
-	0x67, 0x6f, 0x6d, 0x6f, 0x74, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
-	0x08, 0x67, 0x6f, 0x6d, 0x6f, 0x74, 0x65, 0x49, 0x64, 0x12, 0x24, 0x0a, 0x0e, 0x70, 0x75, 0x62,
-	0x6c, 0x69, 0x63, 0x5f, 0x73, 0x73, 0x68, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28,
-	0x0c, 0x52, 0x0c, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x53, 0x73, 0x68, 0x4b, 0x65, 0x79, 0x22,
-	0x47, 0x0a, 0x12, 0x53, 0x69, 0x67, 0x6e, 0x53, 0x53, 0x48, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73,
-	0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x31, 0x0a, 0x15, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x5f,
-	0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x73, 0x73, 0x68, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01,
-	0x20, 0x01, 0x28, 0x0c, 0x52, 0x12, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x50, 0x75, 0x62, 0x6c,
-	0x69, 0x63, 0x53, 0x73, 0x68, 0x4b, 0x65, 0x79, 0x22, 0x13, 0x0a, 0x11, 0x55, 0x70, 0x6c, 0x6f,
-	0x61, 0x64, 0x46, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0xa1, 0x01,
-	0x0a, 0x12, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x46, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70,
-	0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28,
-	0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x12, 0x3e, 0x0a, 0x06, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73,
-	0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e,
+	0x65, 0x73, 0x22, 0x50, 0x0a, 0x13, 0x52, 0x65, 0x61, 0x64, 0x54, 0x47, 0x5a, 0x54, 0x6f, 0x55,
+	0x52, 0x4c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x67, 0x6f, 0x6d,
+	0x6f, 0x74, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x67, 0x6f,
+	0x6d, 0x6f, 0x74, 0x65, 0x49, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74,
+	0x6f, 0x72, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x64, 0x69, 0x72, 0x65, 0x63,
+	0x74, 0x6f, 0x72, 0x79, 0x22, 0x28, 0x0a, 0x14, 0x52, 0x65, 0x61, 0x64, 0x54, 0x47, 0x5a, 0x54,
+	0x6f, 0x55, 0x52, 0x4c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03,
+	0x75, 0x72, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x22, 0x47,
+	0x0a, 0x12, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71,
+	0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x67, 0x6f, 0x6d, 0x6f, 0x74, 0x65, 0x5f, 0x69,
+	0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x67, 0x6f, 0x6d, 0x6f, 0x74, 0x65, 0x49,
+	0x64, 0x12, 0x14, 0x0a, 0x05, 0x70, 0x61, 0x74, 0x68, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09,
+	0x52, 0x05, 0x70, 0x61, 0x74, 0x68, 0x73, 0x22, 0x15, 0x0a, 0x13, 0x52, 0x65, 0x6d, 0x6f, 0x76,
+	0x65, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x56,
+	0x0a, 0x11, 0x53, 0x69, 0x67, 0x6e, 0x53, 0x53, 0x48, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75,
+	0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x67, 0x6f, 0x6d, 0x6f, 0x74, 0x65, 0x5f, 0x69, 0x64,
+	0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x67, 0x6f, 0x6d, 0x6f, 0x74, 0x65, 0x49, 0x64,
+	0x12, 0x24, 0x0a, 0x0e, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x73, 0x73, 0x68, 0x5f, 0x6b,
+	0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0c, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63,
+	0x53, 0x73, 0x68, 0x4b, 0x65, 0x79, 0x22, 0x47, 0x0a, 0x12, 0x53, 0x69, 0x67, 0x6e, 0x53, 0x53,
+	0x48, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x31, 0x0a, 0x15,
+	0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x5f, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x73, 0x73,
+	0x68, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x12, 0x73, 0x69, 0x67,
+	0x6e, 0x65, 0x64, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x53, 0x73, 0x68, 0x4b, 0x65, 0x79, 0x22,
+	0x13, 0x0a, 0x11, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x46, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x71,
+	0x75, 0x65, 0x73, 0x74, 0x22, 0xa1, 0x01, 0x0a, 0x12, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x46,
+	0x69, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x75,
+	0x72, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x12, 0x3e, 0x0a,
+	0x06, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x26, 0x2e,
+	0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x46, 0x69, 0x6c,
+	0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x73,
+	0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x1a, 0x39, 0x0a,
+	0x0b, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03,
+	0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14,
+	0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76,
+	0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x78, 0x0a, 0x17, 0x57, 0x72, 0x69, 0x74,
+	0x65, 0x46, 0x69, 0x6c, 0x65, 0x46, 0x72, 0x6f, 0x6d, 0x55, 0x52, 0x4c, 0x52, 0x65, 0x71, 0x75,
+	0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x67, 0x6f, 0x6d, 0x6f, 0x74, 0x65, 0x5f, 0x69, 0x64,
+	0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x67, 0x6f, 0x6d, 0x6f, 0x74, 0x65, 0x49, 0x64,
+	0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75,
+	0x72, 0x6c, 0x12, 0x1a, 0x0a, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03,
+	0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12,
+	0x0a, 0x04, 0x6d, 0x6f, 0x64, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x07, 0x52, 0x04, 0x6d, 0x6f,
+	0x64, 0x65, 0x22, 0x1a, 0x0a, 0x18, 0x57, 0x72, 0x69, 0x74, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x46,
+	0x72, 0x6f, 0x6d, 0x55, 0x52, 0x4c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x65,
+	0x0a, 0x16, 0x57, 0x72, 0x69, 0x74, 0x65, 0x54, 0x47, 0x5a, 0x46, 0x72, 0x6f, 0x6d, 0x55, 0x52,
+	0x4c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x67, 0x6f, 0x6d, 0x6f,
+	0x74, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x67, 0x6f, 0x6d,
+	0x6f, 0x74, 0x65, 0x49, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x02, 0x20, 0x01,
+	0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x12, 0x1c, 0x0a, 0x09, 0x64, 0x69, 0x72, 0x65, 0x63,
+	0x74, 0x6f, 0x72, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x64, 0x69, 0x72, 0x65,
+	0x63, 0x74, 0x6f, 0x72, 0x79, 0x22, 0x19, 0x0a, 0x17, 0x57, 0x72, 0x69, 0x74, 0x65, 0x54, 0x47,
+	0x5a, 0x46, 0x72, 0x6f, 0x6d, 0x55, 0x52, 0x4c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
+	0x32, 0xa0, 0x08, 0x0a, 0x0d, 0x47, 0x6f, 0x6d, 0x6f, 0x74, 0x65, 0x53, 0x65, 0x72, 0x76, 0x69,
+	0x63, 0x65, 0x12, 0x4b, 0x0a, 0x0c, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61,
+	0x74, 0x65, 0x12, 0x1b, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x41, 0x75, 0x74, 0x68,
+	0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
+	0x1c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74,
+	0x69, 0x63, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12,
+	0x53, 0x0a, 0x0e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63,
+	0x65, 0x12, 0x1d, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74,
+	0x65, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
+	0x1a, 0x1e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65,
+	0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
+	0x22, 0x00, 0x30, 0x01, 0x12, 0x54, 0x0a, 0x0f, 0x44, 0x65, 0x73, 0x74, 0x72, 0x6f, 0x79, 0x49,
+	0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x1e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73,
+	0x2e, 0x44, 0x65, 0x73, 0x74, 0x72, 0x6f, 0x79, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65,
+	0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73,
+	0x2e, 0x44, 0x65, 0x73, 0x74, 0x72, 0x6f, 0x79, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65,
+	0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x53, 0x0a, 0x0e, 0x45, 0x78,
+	0x65, 0x63, 0x75, 0x74, 0x65, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x12, 0x1d, 0x2e, 0x70,
+	0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x43, 0x6f, 0x6d,
+	0x6d, 0x61, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x70, 0x72,
+	0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x43, 0x6f, 0x6d, 0x6d,
+	0x61, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x30, 0x01, 0x12,
+	0x4e, 0x0a, 0x0d, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x41, 0x6c, 0x69, 0x76, 0x65,
+	0x12, 0x1c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e,
+	0x63, 0x65, 0x41, 0x6c, 0x69, 0x76, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d,
+	0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65,
+	0x41, 0x6c, 0x69, 0x76, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12,
+	0x4e, 0x0a, 0x0d, 0x4c, 0x69, 0x73, 0x74, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79,
+	0x12, 0x1c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x44, 0x69,
+	0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d,
+	0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x44, 0x69, 0x72, 0x65,
+	0x63, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12,
+	0x4e, 0x0a, 0x0d, 0x4c, 0x69, 0x73, 0x74, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x73,
+	0x12, 0x1c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x49, 0x6e,
+	0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d,
+	0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x49, 0x6e, 0x73, 0x74,
+	0x61, 0x6e, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12,
+	0x4b, 0x0a, 0x0c, 0x52, 0x65, 0x61, 0x64, 0x54, 0x47, 0x5a, 0x54, 0x6f, 0x55, 0x52, 0x4c, 0x12,
+	0x1b, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x52, 0x65, 0x61, 0x64, 0x54, 0x47, 0x5a,
+	0x54, 0x6f, 0x55, 0x52, 0x4c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x70,
+	0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x52, 0x65, 0x61, 0x64, 0x54, 0x47, 0x5a, 0x54, 0x6f, 0x55,
+	0x52, 0x4c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x48, 0x0a, 0x0b,
+	0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x12, 0x1a, 0x2e, 0x70, 0x72,
+	0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x73,
+	0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73,
+	0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70,
+	0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x45, 0x0a, 0x0a, 0x53, 0x69, 0x67, 0x6e, 0x53, 0x53,
+	0x48, 0x4b, 0x65, 0x79, 0x12, 0x19, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x53, 0x69,
+	0x67, 0x6e, 0x53, 0x53, 0x48, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
+	0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x53, 0x53, 0x48,
+	0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x45, 0x0a,
+	0x0a, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x46, 0x69, 0x6c, 0x65, 0x12, 0x19, 0x2e, 0x70, 0x72,
+	0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x46, 0x69, 0x6c, 0x65, 0x52,
+	0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e,
 	0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x46, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
-	0x73, 0x65, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06,
-	0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x1a, 0x39, 0x0a, 0x0b, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x73,
-	0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01,
-	0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65,
-	0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38,
-	0x01, 0x22, 0x78, 0x0a, 0x17, 0x57, 0x72, 0x69, 0x74, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x46, 0x72,
-	0x6f, 0x6d, 0x55, 0x52, 0x4c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09,
-	0x67, 0x6f, 0x6d, 0x6f, 0x74, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
-	0x08, 0x67, 0x6f, 0x6d, 0x6f, 0x74, 0x65, 0x49, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c,
-	0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x12, 0x1a, 0x0a, 0x08, 0x66,
-	0x69, 0x6c, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x66,
-	0x69, 0x6c, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6d, 0x6f, 0x64, 0x65, 0x18,
-	0x04, 0x20, 0x01, 0x28, 0x07, 0x52, 0x04, 0x6d, 0x6f, 0x64, 0x65, 0x22, 0x1a, 0x0a, 0x18, 0x57,
-	0x72, 0x69, 0x74, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x46, 0x72, 0x6f, 0x6d, 0x55, 0x52, 0x4c, 0x52,
-	0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x65, 0x0a, 0x16, 0x57, 0x72, 0x69, 0x74, 0x65,
-	0x54, 0x47, 0x5a, 0x46, 0x72, 0x6f, 0x6d, 0x55, 0x52, 0x4c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
-	0x74, 0x12, 0x1b, 0x0a, 0x09, 0x67, 0x6f, 0x6d, 0x6f, 0x74, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01,
-	0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x67, 0x6f, 0x6d, 0x6f, 0x74, 0x65, 0x49, 0x64, 0x12, 0x10,
-	0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c,
-	0x12, 0x1c, 0x0a, 0x09, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x18, 0x03, 0x20,
-	0x01, 0x28, 0x09, 0x52, 0x09, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x22, 0x19,
-	0x0a, 0x17, 0x57, 0x72, 0x69, 0x74, 0x65, 0x54, 0x47, 0x5a, 0x46, 0x72, 0x6f, 0x6d, 0x55, 0x52,
-	0x4c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, 0x93, 0x08, 0x0a, 0x0d, 0x47, 0x6f,
-	0x6d, 0x6f, 0x74, 0x65, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x4b, 0x0a, 0x0c, 0x41,
-	0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x12, 0x1b, 0x2e, 0x70, 0x72,
-	0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74,
-	0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
-	0x73, 0x2e, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x52, 0x65,
-	0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x53, 0x0a, 0x0e, 0x43, 0x72, 0x65, 0x61,
-	0x74, 0x65, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x1d, 0x2e, 0x70, 0x72, 0x6f,
-	0x74, 0x6f, 0x73, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e,
-	0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x70, 0x72, 0x6f, 0x74,
-	0x6f, 0x73, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63,
-	0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x30, 0x01, 0x12, 0x54, 0x0a,
-	0x0f, 0x44, 0x65, 0x73, 0x74, 0x72, 0x6f, 0x79, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65,
-	0x12, 0x1e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x44, 0x65, 0x73, 0x74, 0x72, 0x6f,
-	0x79, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
-	0x1a, 0x1f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x44, 0x65, 0x73, 0x74, 0x72, 0x6f,
-	0x79, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
-	0x65, 0x22, 0x00, 0x12, 0x53, 0x0a, 0x0e, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x43, 0x6f,
-	0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x12, 0x1d, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x45,
-	0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x52, 0x65, 0x71,
-	0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x45, 0x78,
-	0x65, 0x63, 0x75, 0x74, 0x65, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70,
-	0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x30, 0x01, 0x12, 0x4e, 0x0a, 0x0d, 0x49, 0x6e, 0x73, 0x74,
-	0x61, 0x6e, 0x63, 0x65, 0x41, 0x6c, 0x69, 0x76, 0x65, 0x12, 0x1c, 0x2e, 0x70, 0x72, 0x6f, 0x74,
-	0x6f, 0x73, 0x2e, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x41, 0x6c, 0x69, 0x76, 0x65,
-	0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73,
-	0x2e, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x41, 0x6c, 0x69, 0x76, 0x65, 0x52, 0x65,
-	0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x4e, 0x0a, 0x0d, 0x4c, 0x69, 0x73, 0x74,
-	0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x12, 0x1c, 0x2e, 0x70, 0x72, 0x6f, 0x74,
-	0x6f, 0x73, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79,
-	0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73,
-	0x2e, 0x4c, 0x69, 0x73, 0x74, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65,
-	0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x4e, 0x0a, 0x0d, 0x4c, 0x69, 0x73, 0x74,
-	0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x73, 0x12, 0x1c, 0x2e, 0x70, 0x72, 0x6f, 0x74,
-	0x6f, 0x73, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x73,
-	0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73,
-	0x2e, 0x4c, 0x69, 0x73, 0x74, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x73, 0x52, 0x65,
-	0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x3e, 0x0a, 0x07, 0x52, 0x65, 0x61, 0x64,
-	0x54, 0x47, 0x5a, 0x12, 0x16, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x52, 0x65, 0x61,
-	0x64, 0x54, 0x47, 0x5a, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x70, 0x72,
-	0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x52, 0x65, 0x61, 0x64, 0x54, 0x47, 0x5a, 0x52, 0x65, 0x73, 0x70,
-	0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x30, 0x01, 0x12, 0x48, 0x0a, 0x0b, 0x52, 0x65, 0x6d, 0x6f,
-	0x76, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x12, 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73,
-	0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75,
-	0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x52, 0x65, 0x6d,
-	0x6f, 0x76, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
-	0x22, 0x00, 0x12, 0x45, 0x0a, 0x0a, 0x53, 0x69, 0x67, 0x6e, 0x53, 0x53, 0x48, 0x4b, 0x65, 0x79,
-	0x12, 0x19, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x53, 0x53,
-	0x48, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x70, 0x72,
-	0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x53, 0x53, 0x48, 0x4b, 0x65, 0x79, 0x52,
-	0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x45, 0x0a, 0x0a, 0x55, 0x70, 0x6c,
-	0x6f, 0x61, 0x64, 0x46, 0x69, 0x6c, 0x65, 0x12, 0x19, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73,
-	0x2e, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x46, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65,
-	0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x55, 0x70, 0x6c, 0x6f,
-	0x61, 0x64, 0x46, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00,
-	0x12, 0x57, 0x0a, 0x10, 0x57, 0x72, 0x69, 0x74, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x46, 0x72, 0x6f,
-	0x6d, 0x55, 0x52, 0x4c, 0x12, 0x1f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x57, 0x72,
-	0x69, 0x74, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x46, 0x72, 0x6f, 0x6d, 0x55, 0x52, 0x4c, 0x52, 0x65,
-	0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x57,
-	0x72, 0x69, 0x74, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x46, 0x72, 0x6f, 0x6d, 0x55, 0x52, 0x4c, 0x52,
-	0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x54, 0x0a, 0x0f, 0x57, 0x72, 0x69,
-	0x74, 0x65, 0x54, 0x47, 0x5a, 0x46, 0x72, 0x6f, 0x6d, 0x55, 0x52, 0x4c, 0x12, 0x1e, 0x2e, 0x70,
-	0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x57, 0x72, 0x69, 0x74, 0x65, 0x54, 0x47, 0x5a, 0x46, 0x72,
-	0x6f, 0x6d, 0x55, 0x52, 0x4c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x70,
-	0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x57, 0x72, 0x69, 0x74, 0x65, 0x54, 0x47, 0x5a, 0x46, 0x72,
-	0x6f, 0x6d, 0x55, 0x52, 0x4c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42,
-	0x2b, 0x5a, 0x29, 0x67, 0x6f, 0x6c, 0x61, 0x6e, 0x67, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x78, 0x2f,
-	0x62, 0x75, 0x69, 0x6c, 0x64, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x67,
-	0x6f, 0x6d, 0x6f, 0x74, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x62, 0x06, 0x70, 0x72,
-	0x6f, 0x74, 0x6f, 0x33,
+	0x73, 0x65, 0x22, 0x00, 0x12, 0x57, 0x0a, 0x10, 0x57, 0x72, 0x69, 0x74, 0x65, 0x46, 0x69, 0x6c,
+	0x65, 0x46, 0x72, 0x6f, 0x6d, 0x55, 0x52, 0x4c, 0x12, 0x1f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+	0x73, 0x2e, 0x57, 0x72, 0x69, 0x74, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x46, 0x72, 0x6f, 0x6d, 0x55,
+	0x52, 0x4c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x70, 0x72, 0x6f, 0x74,
+	0x6f, 0x73, 0x2e, 0x57, 0x72, 0x69, 0x74, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x46, 0x72, 0x6f, 0x6d,
+	0x55, 0x52, 0x4c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x54, 0x0a,
+	0x0f, 0x57, 0x72, 0x69, 0x74, 0x65, 0x54, 0x47, 0x5a, 0x46, 0x72, 0x6f, 0x6d, 0x55, 0x52, 0x4c,
+	0x12, 0x1e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x57, 0x72, 0x69, 0x74, 0x65, 0x54,
+	0x47, 0x5a, 0x46, 0x72, 0x6f, 0x6d, 0x55, 0x52, 0x4c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
+	0x1a, 0x1f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x57, 0x72, 0x69, 0x74, 0x65, 0x54,
+	0x47, 0x5a, 0x46, 0x72, 0x6f, 0x6d, 0x55, 0x52, 0x4c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
+	0x65, 0x22, 0x00, 0x42, 0x2b, 0x5a, 0x29, 0x67, 0x6f, 0x6c, 0x61, 0x6e, 0x67, 0x2e, 0x6f, 0x72,
+	0x67, 0x2f, 0x78, 0x2f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e,
+	0x61, 0x6c, 0x2f, 0x67, 0x6f, 0x6d, 0x6f, 0x74, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73,
+	0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
 }
 
 var (
@@ -1746,8 +1781,8 @@
 	(*ListDirectoryResponse)(nil),      // 13: protos.ListDirectoryResponse
 	(*ListInstancesRequest)(nil),       // 14: protos.ListInstancesRequest
 	(*ListInstancesResponse)(nil),      // 15: protos.ListInstancesResponse
-	(*ReadTGZRequest)(nil),             // 16: protos.ReadTGZRequest
-	(*ReadTGZResponse)(nil),            // 17: protos.ReadTGZResponse
+	(*ReadTGZToURLRequest)(nil),        // 16: protos.ReadTGZToURLRequest
+	(*ReadTGZToURLResponse)(nil),       // 17: protos.ReadTGZToURLResponse
 	(*RemoveFilesRequest)(nil),         // 18: protos.RemoveFilesRequest
 	(*RemoveFilesResponse)(nil),        // 19: protos.RemoveFilesResponse
 	(*SignSSHKeyRequest)(nil),          // 20: protos.SignSSHKeyRequest
@@ -1772,7 +1807,7 @@
 	10, // 8: protos.GomoteService.InstanceAlive:input_type -> protos.InstanceAliveRequest
 	12, // 9: protos.GomoteService.ListDirectory:input_type -> protos.ListDirectoryRequest
 	14, // 10: protos.GomoteService.ListInstances:input_type -> protos.ListInstancesRequest
-	16, // 11: protos.GomoteService.ReadTGZ:input_type -> protos.ReadTGZRequest
+	16, // 11: protos.GomoteService.ReadTGZToURL:input_type -> protos.ReadTGZToURLRequest
 	18, // 12: protos.GomoteService.RemoveFiles:input_type -> protos.RemoveFilesRequest
 	20, // 13: protos.GomoteService.SignSSHKey:input_type -> protos.SignSSHKeyRequest
 	22, // 14: protos.GomoteService.UploadFile:input_type -> protos.UploadFileRequest
@@ -1785,7 +1820,7 @@
 	11, // 21: protos.GomoteService.InstanceAlive:output_type -> protos.InstanceAliveResponse
 	13, // 22: protos.GomoteService.ListDirectory:output_type -> protos.ListDirectoryResponse
 	15, // 23: protos.GomoteService.ListInstances:output_type -> protos.ListInstancesResponse
-	17, // 24: protos.GomoteService.ReadTGZ:output_type -> protos.ReadTGZResponse
+	17, // 24: protos.GomoteService.ReadTGZToURL:output_type -> protos.ReadTGZToURLResponse
 	19, // 25: protos.GomoteService.RemoveFiles:output_type -> protos.RemoveFilesResponse
 	21, // 26: protos.GomoteService.SignSSHKey:output_type -> protos.SignSSHKeyResponse
 	23, // 27: protos.GomoteService.UploadFile:output_type -> protos.UploadFileResponse
@@ -1985,7 +2020,7 @@
 			}
 		}
 		file_gomote_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*ReadTGZRequest); i {
+			switch v := v.(*ReadTGZToURLRequest); i {
 			case 0:
 				return &v.state
 			case 1:
@@ -1997,7 +2032,7 @@
 			}
 		}
 		file_gomote_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*ReadTGZResponse); i {
+			switch v := v.(*ReadTGZToURLResponse); i {
 			case 0:
 				return &v.state
 			case 1:
diff --git a/internal/gomote/protos/gomote.proto b/internal/gomote/protos/gomote.proto
index 896bfcd..9db440f 100644
--- a/internal/gomote/protos/gomote.proto
+++ b/internal/gomote/protos/gomote.proto
@@ -24,8 +24,9 @@
   rpc ListDirectory (ListDirectoryRequest) returns (ListDirectoryResponse) {}
   // ListInstances lists all of the live gomote instances owned by the caller.
   rpc ListInstances (ListInstancesRequest) returns (ListInstancesResponse) {}
-  // ReadTGZ tars and zips a directory which exists on the gomote instance.
-  rpc ReadTGZ (ReadTGZRequest) returns (stream ReadTGZResponse) {}
+  // ReadTGZToURL tars and zips a directory which exists on the gomote instance and returns a URL where it can be
+  // downloaded from.
+  rpc ReadTGZToURL (ReadTGZToURLRequest) returns (ReadTGZToURLResponse) {}
   // RemoveFiles removes files or directories from the gomote instance.
   rpc RemoveFiles (RemoveFilesRequest) returns (RemoveFilesResponse) {}
   // SignSSHKey signs an SSH public key which can be used to SSH into instances owned by the caller.
@@ -158,11 +159,19 @@
   repeated Instance instances = 1;
 }
 
-// ReadTGZRequest specifies the data needed to retrieve a tar and zipped directory from a gomote instance.
-message ReadTGZRequest {}
+// ReadTGZToURLRequest specifies the data needed to retrieve a tar and zipped directory from a gomote instance.
+message ReadTGZToURLRequest {
+  // The unique identifier for a gomote instance.
+  string gomote_id = 1;
+  // The relative directory from the gomote's work directory to tar up.
+  string directory = 2;
+}
 
-// ReadTGZResponse contains a tar and zipped directory from a gomote instance.
-message ReadTGZResponse {}
+// ReadTGZToURLResponse contains a URL where the tar and zipped directory from a gomote instance can be downloaded from.
+message ReadTGZToURLResponse {
+  // URL to retrieve the tarball from.
+  string url = 1;
+}
 
 // RemoveFilesRequest specifies the data needed to remove files or directories from a gomote instance.
 message RemoveFilesRequest {
diff --git a/internal/gomote/protos/gomote_grpc.pb.go b/internal/gomote/protos/gomote_grpc.pb.go
index 0346398..9f14aa5 100644
--- a/internal/gomote/protos/gomote_grpc.pb.go
+++ b/internal/gomote/protos/gomote_grpc.pb.go
@@ -32,8 +32,9 @@
 	ListDirectory(ctx context.Context, in *ListDirectoryRequest, opts ...grpc.CallOption) (*ListDirectoryResponse, error)
 	// ListInstances lists all of the live gomote instances owned by the caller.
 	ListInstances(ctx context.Context, in *ListInstancesRequest, opts ...grpc.CallOption) (*ListInstancesResponse, error)
-	// ReadTGZ tars and zips a directory which exists on the gomote instance.
-	ReadTGZ(ctx context.Context, in *ReadTGZRequest, opts ...grpc.CallOption) (GomoteService_ReadTGZClient, error)
+	// ReadTGZToURL tars and zips a directory which exists on the gomote instance and returns a URL where it can be
+	// downloaded from.
+	ReadTGZToURL(ctx context.Context, in *ReadTGZToURLRequest, opts ...grpc.CallOption) (*ReadTGZToURLResponse, error)
 	// RemoveFiles removes files or directories from the gomote instance.
 	RemoveFiles(ctx context.Context, in *RemoveFilesRequest, opts ...grpc.CallOption) (*RemoveFilesResponse, error)
 	// SignSSHKey signs an SSH public key which can be used to SSH into instances owned by the caller.
@@ -164,36 +165,13 @@
 	return out, nil
 }
 
-func (c *gomoteServiceClient) ReadTGZ(ctx context.Context, in *ReadTGZRequest, opts ...grpc.CallOption) (GomoteService_ReadTGZClient, error) {
-	stream, err := c.cc.NewStream(ctx, &GomoteService_ServiceDesc.Streams[2], "/protos.GomoteService/ReadTGZ", opts...)
+func (c *gomoteServiceClient) ReadTGZToURL(ctx context.Context, in *ReadTGZToURLRequest, opts ...grpc.CallOption) (*ReadTGZToURLResponse, error) {
+	out := new(ReadTGZToURLResponse)
+	err := c.cc.Invoke(ctx, "/protos.GomoteService/ReadTGZToURL", in, out, opts...)
 	if err != nil {
 		return nil, err
 	}
-	x := &gomoteServiceReadTGZClient{stream}
-	if err := x.ClientStream.SendMsg(in); err != nil {
-		return nil, err
-	}
-	if err := x.ClientStream.CloseSend(); err != nil {
-		return nil, err
-	}
-	return x, nil
-}
-
-type GomoteService_ReadTGZClient interface {
-	Recv() (*ReadTGZResponse, error)
-	grpc.ClientStream
-}
-
-type gomoteServiceReadTGZClient struct {
-	grpc.ClientStream
-}
-
-func (x *gomoteServiceReadTGZClient) Recv() (*ReadTGZResponse, error) {
-	m := new(ReadTGZResponse)
-	if err := x.ClientStream.RecvMsg(m); err != nil {
-		return nil, err
-	}
-	return m, nil
+	return out, nil
 }
 
 func (c *gomoteServiceClient) RemoveFiles(ctx context.Context, in *RemoveFilesRequest, opts ...grpc.CallOption) (*RemoveFilesResponse, error) {
@@ -259,8 +237,9 @@
 	ListDirectory(context.Context, *ListDirectoryRequest) (*ListDirectoryResponse, error)
 	// ListInstances lists all of the live gomote instances owned by the caller.
 	ListInstances(context.Context, *ListInstancesRequest) (*ListInstancesResponse, error)
-	// ReadTGZ tars and zips a directory which exists on the gomote instance.
-	ReadTGZ(*ReadTGZRequest, GomoteService_ReadTGZServer) error
+	// ReadTGZToURL tars and zips a directory which exists on the gomote instance and returns a URL where it can be
+	// downloaded from.
+	ReadTGZToURL(context.Context, *ReadTGZToURLRequest) (*ReadTGZToURLResponse, error)
 	// RemoveFiles removes files or directories from the gomote instance.
 	RemoveFiles(context.Context, *RemoveFilesRequest) (*RemoveFilesResponse, error)
 	// SignSSHKey signs an SSH public key which can be used to SSH into instances owned by the caller.
@@ -300,8 +279,8 @@
 func (UnimplementedGomoteServiceServer) ListInstances(context.Context, *ListInstancesRequest) (*ListInstancesResponse, error) {
 	return nil, status.Errorf(codes.Unimplemented, "method ListInstances not implemented")
 }
-func (UnimplementedGomoteServiceServer) ReadTGZ(*ReadTGZRequest, GomoteService_ReadTGZServer) error {
-	return status.Errorf(codes.Unimplemented, "method ReadTGZ not implemented")
+func (UnimplementedGomoteServiceServer) ReadTGZToURL(context.Context, *ReadTGZToURLRequest) (*ReadTGZToURLResponse, error) {
+	return nil, status.Errorf(codes.Unimplemented, "method ReadTGZToURL not implemented")
 }
 func (UnimplementedGomoteServiceServer) RemoveFiles(context.Context, *RemoveFilesRequest) (*RemoveFilesResponse, error) {
 	return nil, status.Errorf(codes.Unimplemented, "method RemoveFiles not implemented")
@@ -463,25 +442,22 @@
 	return interceptor(ctx, in, info, handler)
 }
 
-func _GomoteService_ReadTGZ_Handler(srv interface{}, stream grpc.ServerStream) error {
-	m := new(ReadTGZRequest)
-	if err := stream.RecvMsg(m); err != nil {
-		return err
+func _GomoteService_ReadTGZToURL_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+	in := new(ReadTGZToURLRequest)
+	if err := dec(in); err != nil {
+		return nil, err
 	}
-	return srv.(GomoteServiceServer).ReadTGZ(m, &gomoteServiceReadTGZServer{stream})
-}
-
-type GomoteService_ReadTGZServer interface {
-	Send(*ReadTGZResponse) error
-	grpc.ServerStream
-}
-
-type gomoteServiceReadTGZServer struct {
-	grpc.ServerStream
-}
-
-func (x *gomoteServiceReadTGZServer) Send(m *ReadTGZResponse) error {
-	return x.ServerStream.SendMsg(m)
+	if interceptor == nil {
+		return srv.(GomoteServiceServer).ReadTGZToURL(ctx, in)
+	}
+	info := &grpc.UnaryServerInfo{
+		Server:     srv,
+		FullMethod: "/protos.GomoteService/ReadTGZToURL",
+	}
+	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+		return srv.(GomoteServiceServer).ReadTGZToURL(ctx, req.(*ReadTGZToURLRequest))
+	}
+	return interceptor(ctx, in, info, handler)
 }
 
 func _GomoteService_RemoveFiles_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
@@ -602,6 +578,10 @@
 			Handler:    _GomoteService_ListInstances_Handler,
 		},
 		{
+			MethodName: "ReadTGZToURL",
+			Handler:    _GomoteService_ReadTGZToURL_Handler,
+		},
+		{
 			MethodName: "RemoveFiles",
 			Handler:    _GomoteService_RemoveFiles_Handler,
 		},
@@ -633,11 +613,6 @@
 			Handler:       _GomoteService_ExecuteCommand_Handler,
 			ServerStreams: true,
 		},
-		{
-			StreamName:    "ReadTGZ",
-			Handler:       _GomoteService_ReadTGZ_Handler,
-			ServerStreams: true,
-		},
 	},
 	Metadata: "gomote.proto",
 }