internal/gomote, internal/gomote/protos: add remove files

This changes the remove directory endpoint to remove files. This change
was made because the endpoint should describe the removal of files or
directories from the gomote instances. It also adds the implementation
of remove files to the gomote server.

Updates golang/go#48742

Change-Id: Icaf11afd760bf9837b8c536703774c9d76e588f6
Reviewed-on: https://go-review.googlesource.com/c/build/+/374155
Trust: Carlos Amedee <carlos@golang.org>
Run-TryBot: Carlos Amedee <carlos@golang.org>
Reviewed-by: Dmitri Shuralyov <dmitshur@golang.org>
Reviewed-by: Alex Rakoczy <alex@golang.org>
TryBot-Result: Gopher Robot <gobot@golang.org>
diff --git a/buildlet/fakebuildletclient.go b/buildlet/fakebuildletclient.go
index 5975224..22c206c 100644
--- a/buildlet/fakebuildletclient.go
+++ b/buildlet/fakebuildletclient.go
@@ -57,9 +57,9 @@
 
 // FakeClient is a fake buildlet client used for testing. Not all functions are implemented.
 type FakeClient struct {
-	name         string
-	instanceName string
 	closeFuncs   []func()
+	instanceName string
+	name         string
 }
 
 // AddCloseFunc adds optional extra code to run on close for the fake buildlet.
@@ -123,11 +123,13 @@
 
 // Put places a file on a fake buildlet.
 func (fc *FakeClient) Put(ctx context.Context, r io.Reader, path string, mode os.FileMode) error {
+	// TODO(go.dev/issue/48742) add a file system implementation which would enable proper testing.
 	return errUnimplemented
 }
 
 // PutTar fakes putting  a tar zipped file on a buildldet.
 func (fc *FakeClient) PutTar(ctx context.Context, r io.Reader, dir string) error {
+	// TODO(go.dev/issue/48742) add a file system implementation which would enable proper testing.
 	return errUnimplemented
 }
 
@@ -177,4 +179,7 @@
 func (fc *FakeClient) WorkDir(ctx context.Context) (string, error) { return "", errUnimplemented }
 
 // RemoveAll deletes the provided paths, relative to the work directory for a fake buildlet.
-func (fc *FakeClient) RemoveAll(ctx context.Context, paths ...string) error { return errUnimplemented }
+func (fc *FakeClient) RemoveAll(ctx context.Context, paths ...string) error {
+	// TODO(go.dev/issue/48742) add a file system implementation which would enable proper testing.
+	return nil
+}
diff --git a/internal/gomote/gomote.go b/internal/gomote/gomote.go
index 4b8c2d7..937e988 100644
--- a/internal/gomote/gomote.go
+++ b/internal/gomote/gomote.go
@@ -209,6 +209,35 @@
 	return &protos.DestroyInstanceResponse{}, nil
 }
 
+// RemoveFiles removes files or directories from the gomote instance.
+func (s *Server) RemoveFiles(ctx context.Context, req *protos.RemoveFilesRequest) (*protos.RemoveFilesResponse, error) {
+	creds, err := access.IAPFromContext(ctx)
+	if err != nil {
+		log.Printf("RemoveFiles access.IAPFromContext(ctx) = nil, %s", err)
+		return nil, status.Errorf(codes.Unauthenticated, "request does not contain the required authentication")
+	}
+	// TODO(go.dev/issue/48742) consider what additional path validation should be implemented.
+	if req.GetGomoteId() == "" || len(req.GetPaths()) == 0 {
+		return nil, status.Errorf(codes.InvalidArgument, "invalid arguments")
+	}
+	session, err := s.buildlets.Session(req.GetGomoteId())
+	if err != nil {
+		return nil, status.Errorf(codes.NotFound, "specified gomote instance does not exist")
+	}
+	if session.OwnerID != creds.ID {
+		return nil, status.Errorf(codes.PermissionDenied, "not allowed to modify this gomote session")
+	}
+	bc, err := s.buildlets.BuildletClient(req.GetGomoteId())
+	if err != nil {
+		return nil, status.Errorf(codes.Internal, "unable to retrieve buildlet client")
+	}
+	if err := bc.RemoveAll(ctx, req.GetPaths()...); err != nil {
+		log.Printf("RemoveFiles buildletClient.RemoveAll(ctx, %q) = %s", req.GetPaths(), err)
+		return nil, status.Errorf(codes.Unknown, "unable to remove files")
+	}
+	return &protos.RemoveFilesResponse{}, nil
+}
+
 // WriteTGZFromURL will instruct the gomote instance to download the tar.gz from the provided URL. The tar.gz file will be unpacked in the work directory
 // relative to the directory provided.
 func (s *Server) WriteTGZFromURL(ctx context.Context, req *protos.WriteTGZFromURLRequest) (*protos.WriteTGZFromURLResponse, error) {
diff --git a/internal/gomote/gomote_test.go b/internal/gomote/gomote_test.go
index 7654ef3..b396250 100644
--- a/internal/gomote/gomote_test.go
+++ b/internal/gomote/gomote_test.go
@@ -316,6 +316,86 @@
 	}
 }
 
+func TestRemoveFiles(t *testing.T) {
+	ctx := access.FakeContextWithOutgoingIAPAuth(context.Background(), fakeIAP())
+	client := setupGomoteTest(t, context.Background())
+	gomoteID := mustCreateInstance(t, client, fakeIAP())
+	if _, err := client.RemoveFiles(ctx, &protos.RemoveFilesRequest{
+		GomoteId: gomoteID,
+		Paths:    []string{"temp_file.log"},
+	}); err != nil {
+		t.Fatalf("client.RemoveFiles(ctx, req) = response, %s; want no error", err)
+	}
+}
+
+func TestRemoveFilesError(t *testing.T) {
+	// This test will create a gomote instance and attempt to call RemoveFiles.
+	// If overrideID is set to true, the test will use a diffrent 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.
+		paths      []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.InvalidArgument,
+		},
+		{
+			desc:     "missing paths",
+			ctx:      access.FakeContextWithOutgoingIAPAuth(context.Background(), fakeIAP()),
+			paths:    []string{},
+			wantCode: codes.InvalidArgument,
+		},
+		{
+			desc:       "gomote does not exist",
+			ctx:        access.FakeContextWithOutgoingIAPAuth(context.Background(), fakeIAPWithUser("foo", "bar")),
+			overrideID: true,
+			gomoteID:   "chucky",
+			paths:      []string{"file.a"},
+			wantCode:   codes.NotFound,
+		},
+		{
+			desc:       "wrong gomote id",
+			ctx:        access.FakeContextWithOutgoingIAPAuth(context.Background(), fakeIAPWithUser("foo", "bar")),
+			overrideID: false,
+			paths:      []string{"file.a"},
+			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.RemoveFilesRequest{
+				GomoteId: gomoteID,
+				Paths:    tc.paths,
+			}
+			got, err := client.RemoveFiles(tc.ctx, req)
+			if err != nil && status.Code(err) != tc.wantCode {
+				t.Fatalf("unexpected error: %s", err)
+			}
+			if err == nil {
+				t.Fatalf("client.RemoveFiles(ctx, %v) = %v, nil; want error", req, got)
+			}
+		})
+	}
+}
+
 func TestWriteTGZFromURL(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 0eb8b42..89472db 100644
--- a/internal/gomote/protos/gomote.pb.go
+++ b/internal/gomote/protos/gomote.pb.go
@@ -841,15 +841,22 @@
 	return file_gomote_proto_rawDescGZIP(), []int{16}
 }
 
-// RemoveDirectoryRequest specifies the data needed to remove a directory from a gomote instance.
-type RemoveDirectoryRequest struct {
+// RemoveFilesRequest specifies the data needed to remove files or directories from a gomote instance.
+type RemoveFilesRequest 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 list of paths for files or directories to remove from the file system.
+	// When everything should be deleted, "." should be used.
+	// The paths are relative to the work directory.
+	Paths []string `protobuf:"bytes,2,rep,name=paths,proto3" json:"paths,omitempty"`
 }
 
-func (x *RemoveDirectoryRequest) Reset() {
-	*x = RemoveDirectoryRequest{}
+func (x *RemoveFilesRequest) Reset() {
+	*x = RemoveFilesRequest{}
 	if protoimpl.UnsafeEnabled {
 		mi := &file_gomote_proto_msgTypes[17]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
@@ -857,13 +864,13 @@
 	}
 }
 
-func (x *RemoveDirectoryRequest) String() string {
+func (x *RemoveFilesRequest) String() string {
 	return protoimpl.X.MessageStringOf(x)
 }
 
-func (*RemoveDirectoryRequest) ProtoMessage() {}
+func (*RemoveFilesRequest) ProtoMessage() {}
 
-func (x *RemoveDirectoryRequest) ProtoReflect() protoreflect.Message {
+func (x *RemoveFilesRequest) ProtoReflect() protoreflect.Message {
 	mi := &file_gomote_proto_msgTypes[17]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
@@ -875,20 +882,34 @@
 	return mi.MessageOf(x)
 }
 
-// Deprecated: Use RemoveDirectoryRequest.ProtoReflect.Descriptor instead.
-func (*RemoveDirectoryRequest) Descriptor() ([]byte, []int) {
+// Deprecated: Use RemoveFilesRequest.ProtoReflect.Descriptor instead.
+func (*RemoveFilesRequest) Descriptor() ([]byte, []int) {
 	return file_gomote_proto_rawDescGZIP(), []int{17}
 }
 
-// RemoveDirectoryResponse contains the results from removing a directory from a gomote instance.
-type RemoveDirectoryResponse struct {
+func (x *RemoveFilesRequest) GetGomoteId() string {
+	if x != nil {
+		return x.GomoteId
+	}
+	return ""
+}
+
+func (x *RemoveFilesRequest) GetPaths() []string {
+	if x != nil {
+		return x.Paths
+	}
+	return nil
+}
+
+// RemoveFilesResponse contains the results from removing files or directories from a gomote instance.
+type RemoveFilesResponse struct {
 	state         protoimpl.MessageState
 	sizeCache     protoimpl.SizeCache
 	unknownFields protoimpl.UnknownFields
 }
 
-func (x *RemoveDirectoryResponse) Reset() {
-	*x = RemoveDirectoryResponse{}
+func (x *RemoveFilesResponse) Reset() {
+	*x = RemoveFilesResponse{}
 	if protoimpl.UnsafeEnabled {
 		mi := &file_gomote_proto_msgTypes[18]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
@@ -896,13 +917,13 @@
 	}
 }
 
-func (x *RemoveDirectoryResponse) String() string {
+func (x *RemoveFilesResponse) String() string {
 	return protoimpl.X.MessageStringOf(x)
 }
 
-func (*RemoveDirectoryResponse) ProtoMessage() {}
+func (*RemoveFilesResponse) ProtoMessage() {}
 
-func (x *RemoveDirectoryResponse) ProtoReflect() protoreflect.Message {
+func (x *RemoveFilesResponse) ProtoReflect() protoreflect.Message {
 	mi := &file_gomote_proto_msgTypes[18]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
@@ -914,8 +935,8 @@
 	return mi.MessageOf(x)
 }
 
-// Deprecated: Use RemoveDirectoryResponse.ProtoReflect.Descriptor instead.
-func (*RemoveDirectoryResponse) Descriptor() ([]byte, []int) {
+// Deprecated: Use RemoveFilesResponse.ProtoReflect.Descriptor instead.
+func (*RemoveFilesResponse) Descriptor() ([]byte, []int) {
 	return file_gomote_proto_rawDescGZIP(), []int{18}
 }
 
@@ -1239,91 +1260,93 @@
 	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, 0x18,
-	0x0a, 0x16, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72,
-	0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x19, 0x0a, 0x17, 0x52, 0x65, 0x6d, 0x6f,
-	0x76, 0x65, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f,
-	0x6e, 0x73, 0x65, 0x22, 0x1f, 0x0a, 0x1d, 0x52, 0x65, 0x74, 0x72, 0x69, 0x65, 0x76, 0x65, 0x53,
+	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, 0x1f,
+	0x0a, 0x1d, 0x52, 0x65, 0x74, 0x72, 0x69, 0x65, 0x76, 0x65, 0x53, 0x53, 0x48, 0x43, 0x72, 0x65,
+	0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22,
+	0x20, 0x0a, 0x1e, 0x52, 0x65, 0x74, 0x72, 0x69, 0x65, 0x76, 0x65, 0x53, 0x53, 0x48, 0x43, 0x72,
+	0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
+	0x65, 0x22, 0x11, 0x0a, 0x0f, 0x57, 0x72, 0x69, 0x74, 0x65, 0x54, 0x47, 0x5a, 0x52, 0x65, 0x71,
+	0x75, 0x65, 0x73, 0x74, 0x22, 0x12, 0x0a, 0x10, 0x57, 0x72, 0x69, 0x74, 0x65, 0x54, 0x47, 0x5a,
+	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, 0xda, 0x07, 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, 0x69, 0x0a, 0x16, 0x52, 0x65, 0x74, 0x72, 0x69, 0x65, 0x76, 0x65, 0x53,
+	0x53, 0x48, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x12, 0x25, 0x2e,
+	0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x52, 0x65, 0x74, 0x72, 0x69, 0x65, 0x76, 0x65, 0x53,
 	0x53, 0x48, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x52, 0x65, 0x71,
-	0x75, 0x65, 0x73, 0x74, 0x22, 0x20, 0x0a, 0x1e, 0x52, 0x65, 0x74, 0x72, 0x69, 0x65, 0x76, 0x65,
-	0x53, 0x53, 0x48, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x52, 0x65,
-	0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x11, 0x0a, 0x0f, 0x57, 0x72, 0x69, 0x74, 0x65, 0x54,
-	0x47, 0x5a, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x12, 0x0a, 0x10, 0x57, 0x72, 0x69,
-	0x74, 0x65, 0x54, 0x47, 0x5a, 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,
-	0xe6, 0x07, 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, 0x54,
-	0x0a, 0x0f, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72,
-	0x79, 0x12, 0x1e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76,
-	0x65, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
-	0x74, 0x1a, 0x1f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76,
-	0x65, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
-	0x73, 0x65, 0x22, 0x00, 0x12, 0x69, 0x0a, 0x16, 0x52, 0x65, 0x74, 0x72, 0x69, 0x65, 0x76, 0x65,
-	0x53, 0x53, 0x48, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x12, 0x25,
-	0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x52, 0x65, 0x74, 0x72, 0x69, 0x65, 0x76, 0x65,
-	0x53, 0x53, 0x48, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x52, 0x65,
-	0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x52,
-	0x65, 0x74, 0x72, 0x69, 0x65, 0x76, 0x65, 0x53, 0x53, 0x48, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e,
-	0x74, 0x69, 0x61, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12,
-	0x41, 0x0a, 0x08, 0x57, 0x72, 0x69, 0x74, 0x65, 0x54, 0x47, 0x5a, 0x12, 0x17, 0x2e, 0x70, 0x72,
-	0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x57, 0x72, 0x69, 0x74, 0x65, 0x54, 0x47, 0x5a, 0x52, 0x65, 0x71,
-	0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x57, 0x72,
-	0x69, 0x74, 0x65, 0x54, 0x47, 0x5a, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00,
-	0x28, 0x01, 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,
+	0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x52, 0x65,
+	0x74, 0x72, 0x69, 0x65, 0x76, 0x65, 0x53, 0x53, 0x48, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74,
+	0x69, 0x61, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x41,
+	0x0a, 0x08, 0x57, 0x72, 0x69, 0x74, 0x65, 0x54, 0x47, 0x5a, 0x12, 0x17, 0x2e, 0x70, 0x72, 0x6f,
+	0x74, 0x6f, 0x73, 0x2e, 0x57, 0x72, 0x69, 0x74, 0x65, 0x54, 0x47, 0x5a, 0x52, 0x65, 0x71, 0x75,
+	0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x57, 0x72, 0x69,
+	0x74, 0x65, 0x54, 0x47, 0x5a, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x28,
+	0x01, 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 (
@@ -1359,8 +1382,8 @@
 	(*ListInstancesResponse)(nil),          // 15: protos.ListInstancesResponse
 	(*ReadTGZRequest)(nil),                 // 16: protos.ReadTGZRequest
 	(*ReadTGZResponse)(nil),                // 17: protos.ReadTGZResponse
-	(*RemoveDirectoryRequest)(nil),         // 18: protos.RemoveDirectoryRequest
-	(*RemoveDirectoryResponse)(nil),        // 19: protos.RemoveDirectoryResponse
+	(*RemoveFilesRequest)(nil),             // 18: protos.RemoveFilesRequest
+	(*RemoveFilesResponse)(nil),            // 19: protos.RemoveFilesResponse
 	(*RetrieveSSHCredentialsRequest)(nil),  // 20: protos.RetrieveSSHCredentialsRequest
 	(*RetrieveSSHCredentialsResponse)(nil), // 21: protos.RetrieveSSHCredentialsResponse
 	(*WriteTGZRequest)(nil),                // 22: protos.WriteTGZRequest
@@ -1380,7 +1403,7 @@
 	12, // 8: protos.GomoteService.ListDirectory:input_type -> protos.ListDirectoryRequest
 	14, // 9: protos.GomoteService.ListInstances:input_type -> protos.ListInstancesRequest
 	16, // 10: protos.GomoteService.ReadTGZ:input_type -> protos.ReadTGZRequest
-	18, // 11: protos.GomoteService.RemoveDirectory:input_type -> protos.RemoveDirectoryRequest
+	18, // 11: protos.GomoteService.RemoveFiles:input_type -> protos.RemoveFilesRequest
 	20, // 12: protos.GomoteService.RetrieveSSHCredentials:input_type -> protos.RetrieveSSHCredentialsRequest
 	22, // 13: protos.GomoteService.WriteTGZ:input_type -> protos.WriteTGZRequest
 	24, // 14: protos.GomoteService.WriteTGZFromURL:input_type -> protos.WriteTGZFromURLRequest
@@ -1392,7 +1415,7 @@
 	13, // 20: protos.GomoteService.ListDirectory:output_type -> protos.ListDirectoryResponse
 	15, // 21: protos.GomoteService.ListInstances:output_type -> protos.ListInstancesResponse
 	17, // 22: protos.GomoteService.ReadTGZ:output_type -> protos.ReadTGZResponse
-	19, // 23: protos.GomoteService.RemoveDirectory:output_type -> protos.RemoveDirectoryResponse
+	19, // 23: protos.GomoteService.RemoveFiles:output_type -> protos.RemoveFilesResponse
 	21, // 24: protos.GomoteService.RetrieveSSHCredentials:output_type -> protos.RetrieveSSHCredentialsResponse
 	23, // 25: protos.GomoteService.WriteTGZ:output_type -> protos.WriteTGZResponse
 	25, // 26: protos.GomoteService.WriteTGZFromURL:output_type -> protos.WriteTGZFromURLResponse
@@ -1614,7 +1637,7 @@
 			}
 		}
 		file_gomote_proto_msgTypes[17].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*RemoveDirectoryRequest); i {
+			switch v := v.(*RemoveFilesRequest); i {
 			case 0:
 				return &v.state
 			case 1:
@@ -1626,7 +1649,7 @@
 			}
 		}
 		file_gomote_proto_msgTypes[18].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*RemoveDirectoryResponse); i {
+			switch v := v.(*RemoveFilesResponse); i {
 			case 0:
 				return &v.state
 			case 1:
diff --git a/internal/gomote/protos/gomote.proto b/internal/gomote/protos/gomote.proto
index ff11f94..ae2fcdb 100644
--- a/internal/gomote/protos/gomote.proto
+++ b/internal/gomote/protos/gomote.proto
@@ -26,8 +26,8 @@
   rpc ListInstances (ListInstancesRequest) returns (ListInstancesResponse) {}
   // ReadTGZ tars and zips a dicrectory which exists on the gomote instance.
   rpc ReadTGZ (ReadTGZRequest) returns (stream ReadTGZResponse) {}
-  // RemoveDirectory removes a directory from the gomote instance.
-  rpc RemoveDirectory (RemoveDirectoryRequest) returns (RemoveDirectoryResponse) {}
+  // RemoveFiles removes files or directories from the gomote instance.
+  rpc RemoveFiles (RemoveFilesRequest) returns (RemoveFilesResponse) {}
   // RetrieveSSHCredentials retrieves the SSH credentials for the specified gomote instance.
   rpc RetrieveSSHCredentials (RetrieveSSHCredentialsRequest) returns (RetrieveSSHCredentialsResponse) {}
   // WriteTGZ expands a tar and ziped file onto the file system of a gomote instance.
@@ -120,11 +120,18 @@
 // ReadTGZResponse contains a tar and zipped directory from a gomote instance.
 message ReadTGZResponse {}
 
-// RemoveDirectoryRequest specifies the data needed to remove a directory from a gomote instance.
-message RemoveDirectoryRequest {}
+// RemoveFilesRequest specifies the data needed to remove files or directories from a gomote instance.
+message RemoveFilesRequest {
+  // The unique identifier for a gomote instance.
+  string gomote_id = 1;
+  // The list of paths for files or directories to remove from the file system.
+  // When everything should be deleted, "." should be used.
+  // The paths are relative to the work directory.
+  repeated string paths = 2;
+}
 
-// RemoveDirectoryResponse contains the results from removing a directory from a gomote instance.
-message RemoveDirectoryResponse {}
+// RemoveFilesResponse contains the results from removing files or directories from a gomote instance.
+message RemoveFilesResponse {}
 
 // RetrieveSSHCredentialsRequest specifies the data needed to retrieve SSH credentials for a gomote instance.
 message RetrieveSSHCredentialsRequest {}
diff --git a/internal/gomote/protos/gomote_grpc.pb.go b/internal/gomote/protos/gomote_grpc.pb.go
index a4640d1..2d88d45 100644
--- a/internal/gomote/protos/gomote_grpc.pb.go
+++ b/internal/gomote/protos/gomote_grpc.pb.go
@@ -34,8 +34,8 @@
 	ListInstances(ctx context.Context, in *ListInstancesRequest, opts ...grpc.CallOption) (*ListInstancesResponse, error)
 	// ReadTGZ tars and zips a dicrectory which exists on the gomote instance.
 	ReadTGZ(ctx context.Context, in *ReadTGZRequest, opts ...grpc.CallOption) (GomoteService_ReadTGZClient, error)
-	// RemoveDirectory removes a directory from the gomote instance.
-	RemoveDirectory(ctx context.Context, in *RemoveDirectoryRequest, opts ...grpc.CallOption) (*RemoveDirectoryResponse, error)
+	// RemoveFiles removes files or directories from the gomote instance.
+	RemoveFiles(ctx context.Context, in *RemoveFilesRequest, opts ...grpc.CallOption) (*RemoveFilesResponse, error)
 	// RetrieveSSHCredentials retrieves the SSH credentials for the specified gomote instance.
 	RetrieveSSHCredentials(ctx context.Context, in *RetrieveSSHCredentialsRequest, opts ...grpc.CallOption) (*RetrieveSSHCredentialsResponse, error)
 	// WriteTGZ expands a tar and ziped file onto the file system of a gomote instance.
@@ -193,9 +193,9 @@
 	return m, nil
 }
 
-func (c *gomoteServiceClient) RemoveDirectory(ctx context.Context, in *RemoveDirectoryRequest, opts ...grpc.CallOption) (*RemoveDirectoryResponse, error) {
-	out := new(RemoveDirectoryResponse)
-	err := c.cc.Invoke(ctx, "/protos.GomoteService/RemoveDirectory", in, out, opts...)
+func (c *gomoteServiceClient) RemoveFiles(ctx context.Context, in *RemoveFilesRequest, opts ...grpc.CallOption) (*RemoveFilesResponse, error) {
+	out := new(RemoveFilesResponse)
+	err := c.cc.Invoke(ctx, "/protos.GomoteService/RemoveFiles", in, out, opts...)
 	if err != nil {
 		return nil, err
 	}
@@ -274,8 +274,8 @@
 	ListInstances(context.Context, *ListInstancesRequest) (*ListInstancesResponse, error)
 	// ReadTGZ tars and zips a dicrectory which exists on the gomote instance.
 	ReadTGZ(*ReadTGZRequest, GomoteService_ReadTGZServer) error
-	// RemoveDirectory removes a directory from the gomote instance.
-	RemoveDirectory(context.Context, *RemoveDirectoryRequest) (*RemoveDirectoryResponse, error)
+	// RemoveFiles removes files or directories from the gomote instance.
+	RemoveFiles(context.Context, *RemoveFilesRequest) (*RemoveFilesResponse, error)
 	// RetrieveSSHCredentials retrieves the SSH credentials for the specified gomote instance.
 	RetrieveSSHCredentials(context.Context, *RetrieveSSHCredentialsRequest) (*RetrieveSSHCredentialsResponse, error)
 	// WriteTGZ expands a tar and ziped file onto the file system of a gomote instance.
@@ -313,8 +313,8 @@
 func (UnimplementedGomoteServiceServer) ReadTGZ(*ReadTGZRequest, GomoteService_ReadTGZServer) error {
 	return status.Errorf(codes.Unimplemented, "method ReadTGZ not implemented")
 }
-func (UnimplementedGomoteServiceServer) RemoveDirectory(context.Context, *RemoveDirectoryRequest) (*RemoveDirectoryResponse, error) {
-	return nil, status.Errorf(codes.Unimplemented, "method RemoveDirectory not implemented")
+func (UnimplementedGomoteServiceServer) RemoveFiles(context.Context, *RemoveFilesRequest) (*RemoveFilesResponse, error) {
+	return nil, status.Errorf(codes.Unimplemented, "method RemoveFiles not implemented")
 }
 func (UnimplementedGomoteServiceServer) RetrieveSSHCredentials(context.Context, *RetrieveSSHCredentialsRequest) (*RetrieveSSHCredentialsResponse, error) {
 	return nil, status.Errorf(codes.Unimplemented, "method RetrieveSSHCredentials not implemented")
@@ -491,20 +491,20 @@
 	return x.ServerStream.SendMsg(m)
 }
 
-func _GomoteService_RemoveDirectory_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
-	in := new(RemoveDirectoryRequest)
+func _GomoteService_RemoveFiles_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+	in := new(RemoveFilesRequest)
 	if err := dec(in); err != nil {
 		return nil, err
 	}
 	if interceptor == nil {
-		return srv.(GomoteServiceServer).RemoveDirectory(ctx, in)
+		return srv.(GomoteServiceServer).RemoveFiles(ctx, in)
 	}
 	info := &grpc.UnaryServerInfo{
 		Server:     srv,
-		FullMethod: "/protos.GomoteService/RemoveDirectory",
+		FullMethod: "/protos.GomoteService/RemoveFiles",
 	}
 	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
-		return srv.(GomoteServiceServer).RemoveDirectory(ctx, req.(*RemoveDirectoryRequest))
+		return srv.(GomoteServiceServer).RemoveFiles(ctx, req.(*RemoveFilesRequest))
 	}
 	return interceptor(ctx, in, info, handler)
 }
@@ -599,8 +599,8 @@
 			Handler:    _GomoteService_ListInstances_Handler,
 		},
 		{
-			MethodName: "RemoveDirectory",
-			Handler:    _GomoteService_RemoveDirectory_Handler,
+			MethodName: "RemoveFiles",
+			Handler:    _GomoteService_RemoveFiles_Handler,
 		},
 		{
 			MethodName: "RetrieveSSHCredentials",