From 1cd98852c7b45bf414ca5323db212c33d4ef3e21 Mon Sep 17 00:00:00 2001 From: Ola Rozenfeld Date: Sun, 18 Jun 2023 15:54:59 -0400 Subject: [PATCH 1/5] Node properties: support for leaf nodes --- go/api/command/BUILD.bazel | 15 +- go/api/command/command.pb.go | 312 +++++++++++++++++++---------------- go/api/command/command.proto | 4 + go/pkg/client/tree.go | 46 +++--- go/pkg/client/tree_test.go | 131 ++++++++++----- go/pkg/command/command.go | 34 ++++ go/pkg/rexec/rexec.go | 2 +- go/pkg/tool/BUILD.bazel | 2 + go/pkg/tool/tool.go | 100 +++++++---- go/pkg/tool/tool_test.go | 59 ++++++- 10 files changed, 448 insertions(+), 257 deletions(-) diff --git a/go/api/command/BUILD.bazel b/go/api/command/BUILD.bazel index 9a5d37c98..c8663a872 100644 --- a/go/api/command/BUILD.bazel +++ b/go/api/command/BUILD.bazel @@ -6,14 +6,18 @@ proto_library( name = "command_proto", srcs = ["command.proto"], visibility = ["//visibility:public"], - deps = ["@com_google_protobuf//:timestamp_proto"], + deps = [ + "@com_github_bazelbuild_remote_apis//build/bazel/remote/execution/v2:remote_execution_proto", + "@com_google_protobuf//:timestamp_proto", + ], ) go_proto_library( name = "command_go_proto", importpath = "github.com/bazelbuild/remote-apis-sdks/go/api/command", - proto = ":cmd_proto", + proto = ":command_proto", visibility = ["//visibility:public"], + deps = ["@com_github_bazelbuild_remote_apis//build/bazel/remote/execution/v2:remote_execution_go_proto"], ) go_library( @@ -22,10 +26,3 @@ go_library( importpath = "github.com/bazelbuild/remote-apis-sdks/go/api/command", visibility = ["//visibility:public"], ) - -proto_library( - name = "cmd_proto", - srcs = ["command.proto"], - visibility = ["//visibility:public"], - deps = ["@com_google_protobuf//:timestamp_proto"], -) diff --git a/go/api/command/command.pb.go b/go/api/command/command.pb.go index 30c410190..fe43cb3ae 100755 --- a/go/api/command/command.pb.go +++ b/go/api/command/command.pb.go @@ -1,12 +1,13 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.27.1 +// protoc-gen-go v1.30.0 // protoc v3.17.0 // source: go/api/command/command.proto package command import ( + v2 "github.com/bazelbuild/remote-apis/build/bazel/remote/execution/v2" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" timestamppb "google.golang.org/protobuf/types/known/timestamppb" @@ -588,11 +589,12 @@ type InputSpec struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Inputs []string `protobuf:"bytes,2,rep,name=inputs,proto3" json:"inputs,omitempty"` - VirtualInputs []*VirtualInput `protobuf:"bytes,5,rep,name=virtual_inputs,json=virtualInputs,proto3" json:"virtual_inputs,omitempty"` - ExcludeInputs []*ExcludeInput `protobuf:"bytes,3,rep,name=exclude_inputs,json=excludeInputs,proto3" json:"exclude_inputs,omitempty"` - EnvironmentVariables map[string]string `protobuf:"bytes,4,rep,name=environment_variables,json=environmentVariables,proto3" json:"environment_variables,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` - SymlinkBehavior SymlinkBehaviorType_Value `protobuf:"varint,6,opt,name=symlink_behavior,json=symlinkBehavior,proto3,enum=cmd.SymlinkBehaviorType_Value" json:"symlink_behavior,omitempty"` + Inputs []string `protobuf:"bytes,2,rep,name=inputs,proto3" json:"inputs,omitempty"` + VirtualInputs []*VirtualInput `protobuf:"bytes,5,rep,name=virtual_inputs,json=virtualInputs,proto3" json:"virtual_inputs,omitempty"` + ExcludeInputs []*ExcludeInput `protobuf:"bytes,3,rep,name=exclude_inputs,json=excludeInputs,proto3" json:"exclude_inputs,omitempty"` + EnvironmentVariables map[string]string `protobuf:"bytes,4,rep,name=environment_variables,json=environmentVariables,proto3" json:"environment_variables,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + SymlinkBehavior SymlinkBehaviorType_Value `protobuf:"varint,6,opt,name=symlink_behavior,json=symlinkBehavior,proto3,enum=cmd.SymlinkBehaviorType_Value" json:"symlink_behavior,omitempty"` + InputNodeProperties map[string]*v2.NodeProperties `protobuf:"bytes,7,rep,name=input_node_properties,json=inputNodeProperties,proto3" json:"input_node_properties,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` } func (x *InputSpec) Reset() { @@ -662,6 +664,13 @@ func (x *InputSpec) GetSymlinkBehavior() SymlinkBehaviorType_Value { return SymlinkBehaviorType_UNSPECIFIED } +func (x *InputSpec) GetInputNodeProperties() map[string]*v2.NodeProperties { + if x != nil { + return x.InputNodeProperties + } + return nil +} + type OutputSpec struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -878,131 +887,148 @@ var File_go_api_command_command_proto protoreflect.FileDescriptor var file_go_api_command_command_proto_rawDesc = []byte{ 0x0a, 0x1c, 0x67, 0x6f, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x03, - 0x63, 0x6d, 0x64, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xc6, 0x03, 0x0a, 0x07, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, - 0x12, 0x32, 0x0a, 0x0b, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x63, 0x6d, 0x64, 0x2e, 0x49, 0x64, 0x65, 0x6e, - 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x52, 0x0b, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, - 0x69, 0x65, 0x72, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x65, 0x78, 0x65, 0x63, 0x5f, 0x72, 0x6f, 0x6f, - 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x65, 0x63, 0x52, 0x6f, 0x6f, - 0x74, 0x12, 0x24, 0x0a, 0x05, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x0e, 0x2e, 0x63, 0x6d, 0x64, 0x2e, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x53, 0x70, 0x65, 0x63, - 0x52, 0x05, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x12, 0x27, 0x0a, 0x06, 0x6f, 0x75, 0x74, 0x70, 0x75, - 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x63, 0x6d, 0x64, 0x2e, 0x4f, 0x75, - 0x74, 0x70, 0x75, 0x74, 0x53, 0x70, 0x65, 0x63, 0x52, 0x06, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, - 0x12, 0x12, 0x0a, 0x04, 0x61, 0x72, 0x67, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x04, - 0x61, 0x72, 0x67, 0x73, 0x12, 0x2b, 0x0a, 0x11, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, - 0x6e, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, - 0x10, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, - 0x74, 0x12, 0x2b, 0x0a, 0x11, 0x77, 0x6f, 0x72, 0x6b, 0x69, 0x6e, 0x67, 0x5f, 0x64, 0x69, 0x72, - 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x77, 0x6f, - 0x72, 0x6b, 0x69, 0x6e, 0x67, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x12, 0x36, - 0x0a, 0x08, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x18, 0x08, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x1a, 0x2e, 0x63, 0x6d, 0x64, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x50, - 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x08, 0x70, 0x6c, - 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x12, 0x38, 0x0a, 0x18, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, - 0x5f, 0x77, 0x6f, 0x72, 0x6b, 0x69, 0x6e, 0x67, 0x5f, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, - 0x72, 0x79, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x16, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, - 0x57, 0x6f, 0x72, 0x6b, 0x69, 0x6e, 0x67, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, - 0x1a, 0x3b, 0x0a, 0x0d, 0x50, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 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, 0xf0, 0x01, - 0x0a, 0x0b, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x12, 0x1d, 0x0a, - 0x0a, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x09, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x49, 0x64, 0x12, 0x23, 0x0a, 0x0d, - 0x69, 0x6e, 0x76, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0c, 0x69, 0x6e, 0x76, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, - 0x64, 0x12, 0x3a, 0x0a, 0x19, 0x63, 0x6f, 0x72, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x65, 0x64, 0x5f, - 0x69, 0x6e, 0x76, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x5f, 0x69, 0x64, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x17, 0x63, 0x6f, 0x72, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x65, 0x64, - 0x49, 0x6e, 0x76, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x49, 0x64, 0x12, 0x1b, 0x0a, - 0x09, 0x74, 0x6f, 0x6f, 0x6c, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x08, 0x74, 0x6f, 0x6f, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x74, 0x6f, - 0x6f, 0x6c, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0b, 0x74, 0x6f, 0x6f, 0x6c, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x21, 0x0a, - 0x0c, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x06, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0b, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, - 0x22, 0x3e, 0x0a, 0x09, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x54, 0x79, 0x70, 0x65, 0x22, 0x31, 0x0a, - 0x05, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x0f, 0x0a, 0x0b, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, - 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x44, 0x49, 0x52, 0x45, 0x43, - 0x54, 0x4f, 0x52, 0x59, 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, 0x46, 0x49, 0x4c, 0x45, 0x10, 0x02, - 0x22, 0x4e, 0x0a, 0x0c, 0x45, 0x78, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x49, 0x6e, 0x70, 0x75, 0x74, - 0x12, 0x14, 0x0a, 0x05, 0x72, 0x65, 0x67, 0x65, 0x78, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x05, 0x72, 0x65, 0x67, 0x65, 0x78, 0x12, 0x28, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x0e, 0x32, 0x14, 0x2e, 0x63, 0x6d, 0x64, 0x2e, 0x49, 0x6e, 0x70, 0x75, 0x74, - 0x54, 0x79, 0x70, 0x65, 0x2e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, - 0x22, 0x91, 0x01, 0x0a, 0x0c, 0x56, 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, 0x49, 0x6e, 0x70, 0x75, - 0x74, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, - 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, - 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x69, 0x73, 0x5f, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x61, 0x62, - 0x6c, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x69, 0x73, 0x45, 0x78, 0x65, 0x63, - 0x75, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x2c, 0x0a, 0x12, 0x69, 0x73, 0x5f, 0x65, 0x6d, 0x70, - 0x74, 0x79, 0x5f, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x10, 0x69, 0x73, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x44, 0x69, 0x72, 0x65, 0x63, - 0x74, 0x6f, 0x72, 0x79, 0x22, 0x4a, 0x0a, 0x13, 0x53, 0x79, 0x6d, 0x6c, 0x69, 0x6e, 0x6b, 0x42, - 0x65, 0x68, 0x61, 0x76, 0x69, 0x6f, 0x72, 0x54, 0x79, 0x70, 0x65, 0x22, 0x33, 0x0a, 0x05, 0x56, - 0x61, 0x6c, 0x75, 0x65, 0x12, 0x0f, 0x0a, 0x0b, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, - 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x52, 0x45, 0x53, 0x4f, 0x4c, 0x56, 0x45, - 0x10, 0x01, 0x12, 0x0c, 0x0a, 0x08, 0x50, 0x52, 0x45, 0x53, 0x45, 0x52, 0x56, 0x45, 0x10, 0x02, - 0x22, 0x8a, 0x03, 0x0a, 0x09, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x53, 0x70, 0x65, 0x63, 0x12, 0x16, - 0x0a, 0x06, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, - 0x69, 0x6e, 0x70, 0x75, 0x74, 0x73, 0x12, 0x38, 0x0a, 0x0e, 0x76, 0x69, 0x72, 0x74, 0x75, 0x61, - 0x6c, 0x5f, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, - 0x2e, 0x63, 0x6d, 0x64, 0x2e, 0x56, 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, 0x49, 0x6e, 0x70, 0x75, - 0x74, 0x52, 0x0d, 0x76, 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x73, - 0x12, 0x38, 0x0a, 0x0e, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x5f, 0x69, 0x6e, 0x70, 0x75, - 0x74, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x63, 0x6d, 0x64, 0x2e, 0x45, - 0x78, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x52, 0x0d, 0x65, 0x78, 0x63, - 0x6c, 0x75, 0x64, 0x65, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x73, 0x12, 0x5d, 0x0a, 0x15, 0x65, 0x6e, - 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, - 0x6c, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x63, 0x6d, 0x64, 0x2e, - 0x49, 0x6e, 0x70, 0x75, 0x74, 0x53, 0x70, 0x65, 0x63, 0x2e, 0x45, 0x6e, 0x76, 0x69, 0x72, 0x6f, - 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x45, 0x6e, - 0x74, 0x72, 0x79, 0x52, 0x14, 0x65, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, - 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x12, 0x49, 0x0a, 0x10, 0x73, 0x79, 0x6d, - 0x6c, 0x69, 0x6e, 0x6b, 0x5f, 0x62, 0x65, 0x68, 0x61, 0x76, 0x69, 0x6f, 0x72, 0x18, 0x06, 0x20, - 0x01, 0x28, 0x0e, 0x32, 0x1e, 0x2e, 0x63, 0x6d, 0x64, 0x2e, 0x53, 0x79, 0x6d, 0x6c, 0x69, 0x6e, - 0x6b, 0x42, 0x65, 0x68, 0x61, 0x76, 0x69, 0x6f, 0x72, 0x54, 0x79, 0x70, 0x65, 0x2e, 0x56, 0x61, - 0x6c, 0x75, 0x65, 0x52, 0x0f, 0x73, 0x79, 0x6d, 0x6c, 0x69, 0x6e, 0x6b, 0x42, 0x65, 0x68, 0x61, - 0x76, 0x69, 0x6f, 0x72, 0x1a, 0x47, 0x0a, 0x19, 0x45, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, - 0x65, 0x6e, 0x74, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 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, 0x5e, 0x0a, - 0x0a, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x53, 0x70, 0x65, 0x63, 0x12, 0x21, 0x0a, 0x0c, 0x6f, - 0x75, 0x74, 0x70, 0x75, 0x74, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, - 0x09, 0x52, 0x0b, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x12, 0x2d, - 0x0a, 0x12, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x5f, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, - 0x72, 0x69, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x11, 0x6f, 0x75, 0x74, 0x70, - 0x75, 0x74, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x69, 0x65, 0x73, 0x22, 0x9c, 0x01, - 0x0a, 0x13, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x53, - 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x84, 0x01, 0x0a, 0x05, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, - 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, - 0x53, 0x55, 0x43, 0x43, 0x45, 0x53, 0x53, 0x10, 0x01, 0x12, 0x0d, 0x0a, 0x09, 0x43, 0x41, 0x43, - 0x48, 0x45, 0x5f, 0x48, 0x49, 0x54, 0x10, 0x02, 0x12, 0x11, 0x0a, 0x0d, 0x4e, 0x4f, 0x4e, 0x5f, - 0x5a, 0x45, 0x52, 0x4f, 0x5f, 0x45, 0x58, 0x49, 0x54, 0x10, 0x03, 0x12, 0x0b, 0x0a, 0x07, 0x54, - 0x49, 0x4d, 0x45, 0x4f, 0x55, 0x54, 0x10, 0x04, 0x12, 0x0f, 0x0a, 0x0b, 0x49, 0x4e, 0x54, 0x45, - 0x52, 0x52, 0x55, 0x50, 0x54, 0x45, 0x44, 0x10, 0x05, 0x12, 0x10, 0x0a, 0x0c, 0x52, 0x45, 0x4d, - 0x4f, 0x54, 0x45, 0x5f, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x06, 0x12, 0x0f, 0x0a, 0x0b, 0x4c, - 0x4f, 0x43, 0x41, 0x4c, 0x5f, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x07, 0x22, 0x76, 0x0a, 0x0d, - 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x36, 0x0a, - 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1e, 0x2e, - 0x63, 0x6d, 0x64, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x75, 0x6c, - 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x2e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x06, 0x73, - 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x65, 0x78, 0x69, 0x74, 0x5f, 0x63, 0x6f, - 0x64, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x65, 0x78, 0x69, 0x74, 0x43, 0x6f, - 0x64, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x6d, 0x73, 0x67, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x03, 0x6d, 0x73, 0x67, 0x22, 0x6a, 0x0a, 0x0c, 0x54, 0x69, 0x6d, 0x65, 0x49, 0x6e, 0x74, 0x65, - 0x72, 0x76, 0x61, 0x6c, 0x12, 0x2e, 0x0a, 0x04, 0x66, 0x72, 0x6f, 0x6d, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x04, - 0x66, 0x72, 0x6f, 0x6d, 0x12, 0x2a, 0x0a, 0x02, 0x74, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, + 0x63, 0x6d, 0x64, 0x1a, 0x36, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x2f, 0x62, 0x61, 0x7a, 0x65, 0x6c, + 0x2f, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x2f, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, + 0x6e, 0x2f, 0x76, 0x32, 0x2f, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x5f, 0x65, 0x78, 0x65, 0x63, + 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, + 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, + 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xc6, 0x03, 0x0a, + 0x07, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x12, 0x32, 0x0a, 0x0b, 0x69, 0x64, 0x65, 0x6e, + 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, + 0x63, 0x6d, 0x64, 0x2e, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x52, + 0x0b, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x12, 0x1b, 0x0a, 0x09, + 0x65, 0x78, 0x65, 0x63, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x08, 0x65, 0x78, 0x65, 0x63, 0x52, 0x6f, 0x6f, 0x74, 0x12, 0x24, 0x0a, 0x05, 0x69, 0x6e, 0x70, + 0x75, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x63, 0x6d, 0x64, 0x2e, 0x49, + 0x6e, 0x70, 0x75, 0x74, 0x53, 0x70, 0x65, 0x63, 0x52, 0x05, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x12, + 0x27, 0x0a, 0x06, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x0f, 0x2e, 0x63, 0x6d, 0x64, 0x2e, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x53, 0x70, 0x65, 0x63, + 0x52, 0x06, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x61, 0x72, 0x67, 0x73, + 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x04, 0x61, 0x72, 0x67, 0x73, 0x12, 0x2b, 0x0a, 0x11, + 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, + 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x10, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, + 0x6f, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x12, 0x2b, 0x0a, 0x11, 0x77, 0x6f, 0x72, + 0x6b, 0x69, 0x6e, 0x67, 0x5f, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x18, 0x07, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x77, 0x6f, 0x72, 0x6b, 0x69, 0x6e, 0x67, 0x44, 0x69, 0x72, + 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x12, 0x36, 0x0a, 0x08, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, + 0x72, 0x6d, 0x18, 0x08, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x63, 0x6d, 0x64, 0x2e, 0x43, + 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x50, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x45, + 0x6e, 0x74, 0x72, 0x79, 0x52, 0x08, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x12, 0x38, + 0x0a, 0x18, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x5f, 0x77, 0x6f, 0x72, 0x6b, 0x69, 0x6e, 0x67, + 0x5f, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x16, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x57, 0x6f, 0x72, 0x6b, 0x69, 0x6e, 0x67, 0x44, + 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x1a, 0x3b, 0x0a, 0x0d, 0x50, 0x6c, 0x61, 0x74, + 0x66, 0x6f, 0x72, 0x6d, 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, 0xf0, 0x01, 0x0a, 0x0b, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, + 0x66, 0x69, 0x65, 0x72, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, + 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6f, 0x6d, 0x6d, 0x61, + 0x6e, 0x64, 0x49, 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x69, 0x6e, 0x76, 0x6f, 0x63, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x69, 0x6e, 0x76, + 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x3a, 0x0a, 0x19, 0x63, 0x6f, 0x72, + 0x72, 0x65, 0x6c, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x69, 0x6e, 0x76, 0x6f, 0x63, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x17, 0x63, 0x6f, + 0x72, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x65, 0x64, 0x49, 0x6e, 0x76, 0x6f, 0x63, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x49, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x6f, 0x6f, 0x6c, 0x5f, 0x6e, 0x61, + 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x6f, 0x6f, 0x6c, 0x4e, 0x61, + 0x6d, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x74, 0x6f, 0x6f, 0x6c, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, + 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x74, 0x6f, 0x6f, 0x6c, 0x56, 0x65, + 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x21, 0x0a, 0x0c, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, + 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x65, 0x78, 0x65, + 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x22, 0x3e, 0x0a, 0x09, 0x49, 0x6e, 0x70, 0x75, + 0x74, 0x54, 0x79, 0x70, 0x65, 0x22, 0x31, 0x0a, 0x05, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x0f, + 0x0a, 0x0b, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, + 0x0d, 0x0a, 0x09, 0x44, 0x49, 0x52, 0x45, 0x43, 0x54, 0x4f, 0x52, 0x59, 0x10, 0x01, 0x12, 0x08, + 0x0a, 0x04, 0x46, 0x49, 0x4c, 0x45, 0x10, 0x02, 0x22, 0x4e, 0x0a, 0x0c, 0x45, 0x78, 0x63, 0x6c, + 0x75, 0x64, 0x65, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x72, 0x65, 0x67, 0x65, + 0x78, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x72, 0x65, 0x67, 0x65, 0x78, 0x12, 0x28, + 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x14, 0x2e, 0x63, + 0x6d, 0x64, 0x2e, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x54, 0x79, 0x70, 0x65, 0x2e, 0x56, 0x61, 0x6c, + 0x75, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x22, 0x91, 0x01, 0x0a, 0x0c, 0x56, 0x69, 0x72, + 0x74, 0x75, 0x61, 0x6c, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, + 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x1a, 0x0a, + 0x08, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, + 0x08, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x69, 0x73, 0x5f, + 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x0c, 0x69, 0x73, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x2c, + 0x0a, 0x12, 0x69, 0x73, 0x5f, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x5f, 0x64, 0x69, 0x72, 0x65, 0x63, + 0x74, 0x6f, 0x72, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x69, 0x73, 0x45, 0x6d, + 0x70, 0x74, 0x79, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x22, 0x4a, 0x0a, 0x13, + 0x53, 0x79, 0x6d, 0x6c, 0x69, 0x6e, 0x6b, 0x42, 0x65, 0x68, 0x61, 0x76, 0x69, 0x6f, 0x72, 0x54, + 0x79, 0x70, 0x65, 0x22, 0x33, 0x0a, 0x05, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x0f, 0x0a, 0x0b, + 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x0b, 0x0a, + 0x07, 0x52, 0x45, 0x53, 0x4f, 0x4c, 0x56, 0x45, 0x10, 0x01, 0x12, 0x0c, 0x0a, 0x08, 0x50, 0x52, + 0x45, 0x53, 0x45, 0x52, 0x56, 0x45, 0x10, 0x02, 0x22, 0xe0, 0x04, 0x0a, 0x09, 0x49, 0x6e, 0x70, + 0x75, 0x74, 0x53, 0x70, 0x65, 0x63, 0x12, 0x16, 0x0a, 0x06, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x73, + 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x73, 0x12, 0x38, + 0x0a, 0x0e, 0x76, 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, 0x5f, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x73, + 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x63, 0x6d, 0x64, 0x2e, 0x56, 0x69, 0x72, + 0x74, 0x75, 0x61, 0x6c, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x52, 0x0d, 0x76, 0x69, 0x72, 0x74, 0x75, + 0x61, 0x6c, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x73, 0x12, 0x38, 0x0a, 0x0e, 0x65, 0x78, 0x63, 0x6c, + 0x75, 0x64, 0x65, 0x5f, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x11, 0x2e, 0x63, 0x6d, 0x64, 0x2e, 0x45, 0x78, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x49, 0x6e, + 0x70, 0x75, 0x74, 0x52, 0x0d, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x49, 0x6e, 0x70, 0x75, + 0x74, 0x73, 0x12, 0x5d, 0x0a, 0x15, 0x65, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, + 0x74, 0x5f, 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x28, 0x2e, 0x63, 0x6d, 0x64, 0x2e, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x53, 0x70, 0x65, + 0x63, 0x2e, 0x45, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x56, 0x61, 0x72, + 0x69, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x14, 0x65, 0x6e, 0x76, + 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, + 0x73, 0x12, 0x49, 0x0a, 0x10, 0x73, 0x79, 0x6d, 0x6c, 0x69, 0x6e, 0x6b, 0x5f, 0x62, 0x65, 0x68, + 0x61, 0x76, 0x69, 0x6f, 0x72, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1e, 0x2e, 0x63, 0x6d, + 0x64, 0x2e, 0x53, 0x79, 0x6d, 0x6c, 0x69, 0x6e, 0x6b, 0x42, 0x65, 0x68, 0x61, 0x76, 0x69, 0x6f, + 0x72, 0x54, 0x79, 0x70, 0x65, 0x2e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0f, 0x73, 0x79, 0x6d, + 0x6c, 0x69, 0x6e, 0x6b, 0x42, 0x65, 0x68, 0x61, 0x76, 0x69, 0x6f, 0x72, 0x12, 0x5b, 0x0a, 0x15, + 0x69, 0x6e, 0x70, 0x75, 0x74, 0x5f, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x70, 0x72, 0x6f, 0x70, 0x65, + 0x72, 0x74, 0x69, 0x65, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x63, 0x6d, + 0x64, 0x2e, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x53, 0x70, 0x65, 0x63, 0x2e, 0x49, 0x6e, 0x70, 0x75, + 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x45, + 0x6e, 0x74, 0x72, 0x79, 0x52, 0x13, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x50, + 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x1a, 0x47, 0x0a, 0x19, 0x45, 0x6e, 0x76, + 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, + 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, 0x1a, 0x77, 0x0a, 0x18, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x50, + 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 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, 0x45, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x2f, 0x2e, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x2e, 0x62, 0x61, 0x7a, 0x65, 0x6c, 0x2e, 0x72, 0x65, + 0x6d, 0x6f, 0x74, 0x65, 0x2e, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x76, + 0x32, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, + 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x5e, 0x0a, 0x0a, 0x4f, + 0x75, 0x74, 0x70, 0x75, 0x74, 0x53, 0x70, 0x65, 0x63, 0x12, 0x21, 0x0a, 0x0c, 0x6f, 0x75, 0x74, + 0x70, 0x75, 0x74, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, + 0x0b, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x12, 0x2d, 0x0a, 0x12, + 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x5f, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x69, + 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x11, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, + 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x69, 0x65, 0x73, 0x22, 0x9c, 0x01, 0x0a, 0x13, + 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x53, 0x74, 0x61, + 0x74, 0x75, 0x73, 0x22, 0x84, 0x01, 0x0a, 0x05, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x0b, 0x0a, + 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x53, 0x55, + 0x43, 0x43, 0x45, 0x53, 0x53, 0x10, 0x01, 0x12, 0x0d, 0x0a, 0x09, 0x43, 0x41, 0x43, 0x48, 0x45, + 0x5f, 0x48, 0x49, 0x54, 0x10, 0x02, 0x12, 0x11, 0x0a, 0x0d, 0x4e, 0x4f, 0x4e, 0x5f, 0x5a, 0x45, + 0x52, 0x4f, 0x5f, 0x45, 0x58, 0x49, 0x54, 0x10, 0x03, 0x12, 0x0b, 0x0a, 0x07, 0x54, 0x49, 0x4d, + 0x45, 0x4f, 0x55, 0x54, 0x10, 0x04, 0x12, 0x0f, 0x0a, 0x0b, 0x49, 0x4e, 0x54, 0x45, 0x52, 0x52, + 0x55, 0x50, 0x54, 0x45, 0x44, 0x10, 0x05, 0x12, 0x10, 0x0a, 0x0c, 0x52, 0x45, 0x4d, 0x4f, 0x54, + 0x45, 0x5f, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x06, 0x12, 0x0f, 0x0a, 0x0b, 0x4c, 0x4f, 0x43, + 0x41, 0x4c, 0x5f, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x07, 0x22, 0x76, 0x0a, 0x0d, 0x43, 0x6f, + 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x36, 0x0a, 0x06, 0x73, + 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1e, 0x2e, 0x63, 0x6d, + 0x64, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x53, + 0x74, 0x61, 0x74, 0x75, 0x73, 0x2e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x06, 0x73, 0x74, 0x61, + 0x74, 0x75, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x65, 0x78, 0x69, 0x74, 0x5f, 0x63, 0x6f, 0x64, 0x65, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x65, 0x78, 0x69, 0x74, 0x43, 0x6f, 0x64, 0x65, + 0x12, 0x10, 0x0a, 0x03, 0x6d, 0x73, 0x67, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6d, + 0x73, 0x67, 0x22, 0x6a, 0x0a, 0x0c, 0x54, 0x69, 0x6d, 0x65, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, + 0x61, 0x6c, 0x12, 0x2e, 0x0a, 0x04, 0x66, 0x72, 0x6f, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, - 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x02, 0x74, 0x6f, - 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x04, 0x66, 0x72, + 0x6f, 0x6d, 0x12, 0x2a, 0x0a, 0x02, 0x74, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, + 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, + 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x02, 0x74, 0x6f, 0x62, 0x06, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -1018,7 +1044,7 @@ func file_go_api_command_command_proto_rawDescGZIP() []byte { } var file_go_api_command_command_proto_enumTypes = make([]protoimpl.EnumInfo, 3) -var file_go_api_command_command_proto_msgTypes = make([]protoimpl.MessageInfo, 13) +var file_go_api_command_command_proto_msgTypes = make([]protoimpl.MessageInfo, 14) var file_go_api_command_command_proto_goTypes = []interface{}{ (InputType_Value)(0), // 0: cmd.InputType.Value (SymlinkBehaviorType_Value)(0), // 1: cmd.SymlinkBehaviorType.Value @@ -1036,7 +1062,9 @@ var file_go_api_command_command_proto_goTypes = []interface{}{ (*TimeInterval)(nil), // 13: cmd.TimeInterval nil, // 14: cmd.Command.PlatformEntry nil, // 15: cmd.InputSpec.EnvironmentVariablesEntry - (*timestamppb.Timestamp)(nil), // 16: google.protobuf.Timestamp + nil, // 16: cmd.InputSpec.InputNodePropertiesEntry + (*timestamppb.Timestamp)(nil), // 17: google.protobuf.Timestamp + (*v2.NodeProperties)(nil), // 18: build.bazel.remote.execution.v2.NodeProperties } var file_go_api_command_command_proto_depIdxs = []int32{ 4, // 0: cmd.Command.identifiers:type_name -> cmd.Identifiers @@ -1048,14 +1076,16 @@ var file_go_api_command_command_proto_depIdxs = []int32{ 6, // 6: cmd.InputSpec.exclude_inputs:type_name -> cmd.ExcludeInput 15, // 7: cmd.InputSpec.environment_variables:type_name -> cmd.InputSpec.EnvironmentVariablesEntry 1, // 8: cmd.InputSpec.symlink_behavior:type_name -> cmd.SymlinkBehaviorType.Value - 2, // 9: cmd.CommandResult.status:type_name -> cmd.CommandResultStatus.Value - 16, // 10: cmd.TimeInterval.from:type_name -> google.protobuf.Timestamp - 16, // 11: cmd.TimeInterval.to:type_name -> google.protobuf.Timestamp - 12, // [12:12] is the sub-list for method output_type - 12, // [12:12] is the sub-list for method input_type - 12, // [12:12] is the sub-list for extension type_name - 12, // [12:12] is the sub-list for extension extendee - 0, // [0:12] is the sub-list for field type_name + 16, // 9: cmd.InputSpec.input_node_properties:type_name -> cmd.InputSpec.InputNodePropertiesEntry + 2, // 10: cmd.CommandResult.status:type_name -> cmd.CommandResultStatus.Value + 17, // 11: cmd.TimeInterval.from:type_name -> google.protobuf.Timestamp + 17, // 12: cmd.TimeInterval.to:type_name -> google.protobuf.Timestamp + 18, // 13: cmd.InputSpec.InputNodePropertiesEntry.value:type_name -> build.bazel.remote.execution.v2.NodeProperties + 14, // [14:14] is the sub-list for method output_type + 14, // [14:14] is the sub-list for method input_type + 14, // [14:14] is the sub-list for extension type_name + 14, // [14:14] is the sub-list for extension extendee + 0, // [0:14] is the sub-list for field type_name } func init() { file_go_api_command_command_proto_init() } @@ -1203,7 +1233,7 @@ func file_go_api_command_command_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_go_api_command_command_proto_rawDesc, NumEnums: 3, - NumMessages: 13, + NumMessages: 14, NumExtensions: 0, NumServices: 0, }, diff --git a/go/api/command/command.proto b/go/api/command/command.proto index b5dbbbf2e..32494b79c 100644 --- a/go/api/command/command.proto +++ b/go/api/command/command.proto @@ -2,6 +2,7 @@ syntax = "proto3"; package cmd; +import "build/bazel/remote/execution/v2/remote_execution.proto"; import "google/protobuf/timestamp.proto"; // A command to execute locally or remotely. @@ -123,6 +124,9 @@ message InputSpec { // Determines how symlinks should be treated when constructing the input tree. SymlinkBehaviorType.Value symlink_behavior = 6; + + // Node properties of inputs. + map input_node_properties = 7; } message OutputSpec { diff --git a/go/pkg/client/tree.go b/go/pkg/client/tree.go index 2c8b9e033..201957f95 100644 --- a/go/pkg/client/tree.go +++ b/go/pkg/client/tree.go @@ -40,6 +40,7 @@ type fileSysNode struct { file *fileNode emptyDirectoryMarker bool symlink *symlinkNode + nodeProperties *repb.NodeProperties } // TreeStats contains various stats/metadata of the constructed Merkle tree. @@ -164,7 +165,7 @@ func getExecRootRelPaths(absPath, execRoot, workingDir, remoteWorkingDir string) // loadFiles reads all files specified by the given InputSpec (descending into subdirectories // recursively), and loads their contents into the provided map. -func loadFiles(execRoot, localWorkingDir, remoteWorkingDir string, excl []*command.InputExclusion, filesToProcess []string, fs map[string]*fileSysNode, cache filemetadata.Cache, opts *TreeSymlinkOpts) error { +func loadFiles(execRoot, localWorkingDir, remoteWorkingDir string, excl []*command.InputExclusion, filesToProcess []string, fs map[string]*fileSysNode, cache filemetadata.Cache, opts *TreeSymlinkOpts, nodeProperties map[string]*repb.NodeProperties) error { if opts == nil { opts = DefaultTreeSymlinkOpts() } @@ -181,6 +182,7 @@ func loadFiles(execRoot, localWorkingDir, remoteWorkingDir string, excl []*comma if err != nil { return err } + np := nodeProperties[remoteNormPath] meta := cache.Get(absPath) // An implication of this is that, if a path is a symlink to a @@ -213,7 +215,8 @@ func loadFiles(execRoot, localWorkingDir, remoteWorkingDir string, excl []*comma // an absolute path. Since the remote worker will map the exec root // to a different directory, we must strip away the local exec root. // See https://github.com/bazelbuild/remote-apis-sdks/pull/229#discussion_r524830458 - symlink: &symlinkNode{target: targetSymDir}, + symlink: &symlinkNode{target: targetSymDir}, + nodeProperties: np, } if !meta.Symlink.IsDangling && opts.FollowsTarget { @@ -248,7 +251,7 @@ func loadFiles(execRoot, localWorkingDir, remoteWorkingDir string, excl []*comma if len(files) == 0 { if normPath != "." { - fs[remoteNormPath] = &fileSysNode{emptyDirectoryMarker: true} + fs[remoteNormPath] = &fileSysNode{emptyDirectoryMarker: true, nodeProperties: np} } continue } @@ -267,6 +270,7 @@ func loadFiles(execRoot, localWorkingDir, remoteWorkingDir string, excl []*comma ue: uploadinfo.EntryFromFile(meta.Digest, absPath), isExecutable: meta.IsExecutable, }, + nodeProperties: np, } } } @@ -286,9 +290,10 @@ func (c *Client) ComputeMerkleTree(execRoot, workingDir, remoteWorkingDir string if err != nil { return digest.Empty, nil, nil, err } + np := is.InputNodeProperties[remoteNormPath] if i.IsEmptyDirectory { if normPath != "." { - fs[remoteNormPath] = &fileSysNode{emptyDirectoryMarker: true} + fs[remoteNormPath] = &fileSysNode{emptyDirectoryMarker: true, nodeProperties: np} } continue } @@ -297,9 +302,10 @@ func (c *Client) ComputeMerkleTree(execRoot, workingDir, remoteWorkingDir string ue: uploadinfo.EntryFromBlob(i.Contents), isExecutable: i.IsExecutable, }, + nodeProperties: np, } } - if err := loadFiles(execRoot, workingDir, remoteWorkingDir, is.InputExclusions, is.Inputs, fs, cache, treeSymlinkOpts(c.TreeSymlinkOpts, is.SymlinkBehavior)); err != nil { + if err := loadFiles(execRoot, workingDir, remoteWorkingDir, is.InputExclusions, is.Inputs, fs, cache, treeSymlinkOpts(c.TreeSymlinkOpts, is.SymlinkBehavior), is.InputNodeProperties); err != nil { return digest.Empty, nil, nil, err } ft, err := buildTree(fs) @@ -371,17 +377,16 @@ func packageTree(t *treeNode, stats *TreeStats) (root digest.Digest, blobs map[d sort.Slice(dir.Directories, func(i, j int) bool { return dir.Directories[i].Name < dir.Directories[j].Name }) for name, n := range t.leaves { - // A node can have exactly one of file/symlink/emptyDirectoryMarker. if n.file != nil { dg := n.file.ue.Digest - dir.Files = append(dir.Files, &repb.FileNode{Name: name, Digest: dg.ToProto(), IsExecutable: n.file.isExecutable}) + dir.Files = append(dir.Files, &repb.FileNode{Name: name, Digest: dg.ToProto(), IsExecutable: n.file.isExecutable, NodeProperties: n.nodeProperties}) blobs[dg] = n.file.ue stats.InputFiles++ stats.TotalInputBytes += dg.Size continue } if n.symlink != nil { - dir.Symlinks = append(dir.Symlinks, &repb.SymlinkNode{Name: name, Target: n.symlink.target}) + dir.Symlinks = append(dir.Symlinks, &repb.SymlinkNode{Name: name, Target: n.symlink.target, NodeProperties: n.nodeProperties}) stats.InputSymlinks++ } } @@ -407,6 +412,7 @@ type TreeOutput struct { IsExecutable bool IsEmptyDirectory bool SymlinkTarget string + NodeProperties *repb.NodeProperties } // FlattenTree takes a Tree message and calculates the relative paths of all the files to @@ -456,15 +462,17 @@ func flattenTree(root digest.Digest, rootPath string, dirs map[digest.Digest]*re Path: flatDir.p, Digest: digest.Empty, IsEmptyDirectory: true, + NodeProperties: dir.NodeProperties, } continue } // Add files to the set to return for _, file := range dir.Files { out := &TreeOutput{ - Path: filepath.Join(flatDir.p, file.Name), - Digest: digest.NewFromProtoUnvalidated(file.Digest), - IsExecutable: file.IsExecutable, + Path: filepath.Join(flatDir.p, file.Name), + Digest: digest.NewFromProtoUnvalidated(file.Digest), + IsExecutable: file.IsExecutable, + NodeProperties: file.NodeProperties, } flatFiles[out.Path] = out } @@ -472,8 +480,9 @@ func flattenTree(root digest.Digest, rootPath string, dirs map[digest.Digest]*re // Add symlinks to the set to return for _, sm := range dir.Symlinks { out := &TreeOutput{ - Path: filepath.Join(flatDir.p, sm.Name), - SymlinkTarget: sm.Target, + Path: filepath.Join(flatDir.p, sm.Name), + SymlinkTarget: sm.Target, + NodeProperties: sm.NodeProperties, } flatFiles[out.Path] = out } @@ -520,15 +529,14 @@ func packageDirectories(t *treeNode) (root *repb.Directory, files map[digest.Dig sort.Slice(root.Directories, func(i, j int) bool { return root.Directories[i].Name < root.Directories[j].Name }) for name, n := range t.leaves { - // A node can have exactly one of file/symlink/emptyDirectoryMarker. if n.file != nil { dg := n.file.ue.Digest - root.Files = append(root.Files, &repb.FileNode{Name: name, Digest: dg.ToProto(), IsExecutable: n.file.isExecutable}) + root.Files = append(root.Files, &repb.FileNode{Name: name, Digest: dg.ToProto(), IsExecutable: n.file.isExecutable, NodeProperties: n.nodeProperties}) files[dg] = n.file.ue continue } if n.symlink != nil { - root.Symlinks = append(root.Symlinks, &repb.SymlinkNode{Name: name, Target: n.symlink.target}) + root.Symlinks = append(root.Symlinks, &repb.SymlinkNode{Name: name, Target: n.symlink.target, NodeProperties: n.nodeProperties}) } } sort.Slice(root.Files, func(i, j int) bool { return root.Files[i].Name < root.Files[j].Name }) @@ -540,7 +548,7 @@ func packageDirectories(t *treeNode) (root *repb.Directory, files map[digest.Dig // ComputeOutputsToUpload transforms the provided local output paths into uploadable Chunkers. // The paths have to be relative to execRoot. // It also populates the remote ActionResult, packaging output directories as trees where required. -func (c *Client) ComputeOutputsToUpload(execRoot, workingDir string, paths []string, cache filemetadata.Cache, sb command.SymlinkBehaviorType) (map[digest.Digest]*uploadinfo.Entry, *repb.ActionResult, error) { +func (c *Client) ComputeOutputsToUpload(execRoot, workingDir string, paths []string, cache filemetadata.Cache, sb command.SymlinkBehaviorType, nodeProperties map[string]*repb.NodeProperties) (map[digest.Digest]*uploadinfo.Entry, *repb.ActionResult, error) { outs := make(map[digest.Digest]*uploadinfo.Entry) resPb := &repb.ActionResult{} for _, path := range paths { @@ -563,12 +571,12 @@ func (c *Client) ComputeOutputsToUpload(execRoot, workingDir string, paths []str // A regular file. ue := uploadinfo.EntryFromFile(meta.Digest, absPath) outs[meta.Digest] = ue - resPb.OutputFiles = append(resPb.OutputFiles, &repb.OutputFile{Path: normPath, Digest: meta.Digest.ToProto(), IsExecutable: meta.IsExecutable}) + resPb.OutputFiles = append(resPb.OutputFiles, &repb.OutputFile{Path: normPath, Digest: meta.Digest.ToProto(), IsExecutable: meta.IsExecutable, NodeProperties: nodeProperties[normPath]}) continue } // A directory. fs := make(map[string]*fileSysNode) - if e := loadFiles(absPath, "", "", nil, []string{"."}, fs, cache, treeSymlinkOpts(c.TreeSymlinkOpts, sb)); e != nil { + if e := loadFiles(absPath, "", "", nil, []string{"."}, fs, cache, treeSymlinkOpts(c.TreeSymlinkOpts, sb), nodeProperties); e != nil { return nil, nil, e } ft, err := buildTree(fs) diff --git a/go/pkg/client/tree_test.go b/go/pkg/client/tree_test.go index 8376886a9..f84c9ecce 100644 --- a/go/pkg/client/tree_test.go +++ b/go/pkg/client/tree_test.go @@ -25,14 +25,15 @@ var ( fooBlob, barBlob, bazBlob = []byte("foo"), []byte("bar"), []byte("baz") fooDg, barDg, bazDg = digest.NewFromBlob(fooBlob), digest.NewFromBlob(barBlob), digest.NewFromBlob(bazBlob) fooDgPb, barDgPb, bazDgPb = fooDg.ToProto(), barDg.ToProto(), bazDg.ToProto() + fooProperties = &repb.NodeProperties{Properties: []*repb.NodeProperty{{Name: "fooName", Value: "fooValue"}}} - fooDir = &repb.Directory{Files: []*repb.FileNode{{Name: "foo", Digest: fooDgPb, IsExecutable: true}}} + fooDir = &repb.Directory{Files: []*repb.FileNode{{Name: "foo", Digest: fooDgPb, IsExecutable: true, NodeProperties: fooProperties}}} barDir = &repb.Directory{Files: []*repb.FileNode{{Name: "bar", Digest: barDgPb}}} bazDir = &repb.Directory{Files: []*repb.FileNode{{Name: "baz", Digest: bazDgPb}}} vBarDir = &repb.Directory{Directories: []*repb.DirectoryNode{{Name: "baz", Digest: digest.Empty.ToProto()}}} foobarDir = &repb.Directory{Files: []*repb.FileNode{ {Name: "bar", Digest: barDgPb}, - {Name: "foo", Digest: fooDgPb, IsExecutable: true}, + {Name: "foo", Digest: fooDgPb, IsExecutable: true, NodeProperties: fooProperties}, }} fooDirBlob, barDirBlob, foobarDirBlob, bazDirBlob, vBarDirBlob = mustMarshal(fooDir), mustMarshal(barDir), mustMarshal(foobarDir), mustMarshal(bazDir), mustMarshal(vBarDir) @@ -473,7 +474,8 @@ func TestComputeMerkleTree(t *testing.T) { {path: "bar", fileContents: barBlob}, }, spec: &command.InputSpec{ - Inputs: []string{"foo", "bar"}, + Inputs: []string{"foo", "bar"}, + InputNodeProperties: map[string]*repb.NodeProperties{"foo": fooProperties}, }, rootDir: foobarDir, additionalBlobs: [][]byte{fooBlob, barBlob}, @@ -494,7 +496,8 @@ func TestComputeMerkleTree(t *testing.T) { {path: "barDir/bar", fileContents: barBlob}, }, spec: &command.InputSpec{ - Inputs: []string{"fooDir", "barDir"}, + Inputs: []string{"fooDir", "barDir"}, + InputNodeProperties: map[string]*repb.NodeProperties{"fooDir/foo": fooProperties}, }, rootDir: &repb.Directory{Directories: []*repb.DirectoryNode{ {Name: "barDir", Digest: barDirDgPb}, @@ -521,7 +524,8 @@ func TestComputeMerkleTree(t *testing.T) { {path: "barDir/bar", fileContents: barBlob}, }, spec: &command.InputSpec{ - Inputs: []string{"fooDir/../fooDir/foo", "//barDir//bar"}, + Inputs: []string{"fooDir/../fooDir/foo", "//barDir//bar"}, + InputNodeProperties: map[string]*repb.NodeProperties{"fooDir/foo": fooProperties}, }, rootDir: &repb.Directory{Directories: []*repb.DirectoryNode{ {Name: "barDir", Digest: barDirDgPb}, @@ -545,7 +549,8 @@ func TestComputeMerkleTree(t *testing.T) { {path: "foo", isSymlink: true, isAbsolute: true, symlinkTarget: "fooDir/foo"}, }, spec: &command.InputSpec{ - Inputs: []string{"fooDir", "foo"}, + Inputs: []string{"fooDir", "foo"}, + InputNodeProperties: map[string]*repb.NodeProperties{"fooDir/foo": fooProperties}, }, rootDir: &repb.Directory{ Directories: []*repb.DirectoryNode{{Name: "fooDir", Digest: fooDirDgPb}}, @@ -570,7 +575,8 @@ func TestComputeMerkleTree(t *testing.T) { {path: "foo", isSymlink: true, symlinkTarget: "fooDir/foo"}, }, spec: &command.InputSpec{ - Inputs: []string{"fooDir", "foo"}, + Inputs: []string{"fooDir", "foo"}, + InputNodeProperties: map[string]*repb.NodeProperties{"fooDir/foo": fooProperties}, }, rootDir: &repb.Directory{ Directories: []*repb.DirectoryNode{{Name: "fooDir", Digest: fooDirDgPb}}, @@ -596,7 +602,8 @@ func TestComputeMerkleTree(t *testing.T) { }, spec: &command.InputSpec{ // The symlink target will be traversed recursively. - Inputs: []string{"fooSym"}, + Inputs: []string{"fooSym"}, + InputNodeProperties: map[string]*repb.NodeProperties{"fooDir/foo": fooProperties}, }, rootDir: &repb.Directory{ Directories: []*repb.DirectoryNode{{Name: "fooDir", Digest: fooDirDgPb}}, @@ -626,8 +633,9 @@ func TestComputeMerkleTree(t *testing.T) { }, spec: &command.InputSpec{ // The symlink target will be traversed recursively. - Inputs: []string{"fooSym"}, - SymlinkBehavior: command.PreserveSymlink, + Inputs: []string{"fooSym"}, + SymlinkBehavior: command.PreserveSymlink, + InputNodeProperties: map[string]*repb.NodeProperties{"fooDir/foo": fooProperties}, }, rootDir: &repb.Directory{ Directories: []*repb.DirectoryNode{{Name: "fooDir", Digest: fooDirDgPb}}, @@ -655,7 +663,8 @@ func TestComputeMerkleTree(t *testing.T) { {path: "fooSym", isSymlink: true, symlinkTarget: "fooDir/foo"}, }, spec: &command.InputSpec{ - Inputs: []string{"fooSym"}, + Inputs: []string{"fooSym"}, + InputNodeProperties: map[string]*repb.NodeProperties{"fooDir/foo": fooProperties}, }, rootDir: &repb.Directory{ Directories: nil, @@ -682,7 +691,8 @@ func TestComputeMerkleTree(t *testing.T) { }, spec: &command.InputSpec{ // The symlink target will be traversed recursively. - Inputs: []string{"fooSym"}, + Inputs: []string{"fooSym"}, + InputNodeProperties: map[string]*repb.NodeProperties{"fooDir/foo": fooProperties}, }, rootDir: &repb.Directory{ Directories: []*repb.DirectoryNode{{Name: "fooDir", Digest: fooDirDgPb}}, @@ -712,7 +722,8 @@ func TestComputeMerkleTree(t *testing.T) { {path: "bar", isSymlink: true, symlinkTarget: "fooDir/bar"}, }, spec: &command.InputSpec{ - Inputs: []string{"fooDir", "foo"}, + Inputs: []string{"fooDir", "foo"}, + InputNodeProperties: map[string]*repb.NodeProperties{"fooDir/foo": fooProperties}, }, rootDir: &repb.Directory{ Directories: []*repb.DirectoryNode{{Name: "fooDir", Digest: fooDirDgPb}}, @@ -737,7 +748,8 @@ func TestComputeMerkleTree(t *testing.T) { {path: "invalidSym", isSymlink: true, symlinkTarget: "fooDir/invalid"}, }, spec: &command.InputSpec{ - Inputs: []string{"fooDir", "invalidSym"}, + Inputs: []string{"fooDir", "invalidSym"}, + InputNodeProperties: map[string]*repb.NodeProperties{"fooDir/foo": fooProperties}, }, rootDir: &repb.Directory{ Directories: []*repb.DirectoryNode{{Name: "fooDir", Digest: fooDirDgPb}}, @@ -768,7 +780,8 @@ func TestComputeMerkleTree(t *testing.T) { {path: "barDir", isSymlink: true, isAbsolute: true, symlinkTarget: "barDirTarget"}, }, spec: &command.InputSpec{ - Inputs: []string{"fooDir", "barDir"}, + Inputs: []string{"fooDir", "barDir"}, + InputNodeProperties: map[string]*repb.NodeProperties{"fooDir/foo": fooProperties}, }, rootDir: &repb.Directory{Directories: []*repb.DirectoryNode{ {Name: "barDir", Digest: barDirDgPb}, @@ -795,7 +808,8 @@ func TestComputeMerkleTree(t *testing.T) { {path: "barDir", isSymlink: true, symlinkTarget: "barDirTarget"}, }, spec: &command.InputSpec{ - Inputs: []string{"fooDir", "barDir"}, + Inputs: []string{"fooDir", "barDir"}, + InputNodeProperties: map[string]*repb.NodeProperties{"fooDir/foo": fooProperties}, }, rootDir: &repb.Directory{Directories: []*repb.DirectoryNode{ {Name: "barDir", Digest: barDirDgPb}, @@ -823,7 +837,8 @@ func TestComputeMerkleTree(t *testing.T) { }, spec: &command.InputSpec{ // The symlink target will be traversed recursively. - Inputs: []string{"base/foobarSymDir"}, + Inputs: []string{"base/foobarSymDir"}, + InputNodeProperties: map[string]*repb.NodeProperties{"foobarDir/foo": fooProperties}, }, rootDir: &repb.Directory{ Directories: []*repb.DirectoryNode{{Name: "base", Digest: foobarSymDirDgPb}, {Name: "foobarDir", Digest: foobarDirDgPb}}, @@ -855,7 +870,8 @@ func TestComputeMerkleTree(t *testing.T) { }, spec: &command.InputSpec{ // The symlink target will be traversed recursively. - Inputs: []string{"base/foobarSymDir"}, + Inputs: []string{"base/foobarSymDir"}, + InputNodeProperties: map[string]*repb.NodeProperties{"foobarDir/foo": fooProperties}, }, rootDir: &repb.Directory{ Directories: []*repb.DirectoryNode{{Name: "base", Digest: foobarSymDirDgPb}, {Name: "foobarDir", Digest: foobarDirDgPb}}, @@ -926,7 +942,8 @@ func TestComputeMerkleTree(t *testing.T) { {path: "foobarDir/bar", fileContents: barBlob}, }, spec: &command.InputSpec{ - Inputs: []string{"fooDir", "foobarDir"}, + Inputs: []string{"fooDir", "foobarDir"}, + InputNodeProperties: map[string]*repb.NodeProperties{"fooDir/foo": fooProperties, "foobarDir/foo": fooProperties}, }, rootDir: &repb.Directory{Directories: []*repb.DirectoryNode{ {Name: "fooDir", Digest: fooDirDgPb}, @@ -953,7 +970,8 @@ func TestComputeMerkleTree(t *testing.T) { {path: "fooDir2/foo", fileContents: fooBlob, isExecutable: true}, }, spec: &command.InputSpec{ - Inputs: []string{"fooDir1", "fooDir2"}, + Inputs: []string{"fooDir1", "fooDir2"}, + InputNodeProperties: map[string]*repb.NodeProperties{"fooDir1/foo": fooProperties, "fooDir2/foo": fooProperties}, }, rootDir: &repb.Directory{Directories: []*repb.DirectoryNode{ {Name: "fooDir1", Digest: fooDirDgPb}, @@ -979,7 +997,8 @@ func TestComputeMerkleTree(t *testing.T) { {path: "fooDir/foo", fileContents: fooBlob, isExecutable: true}, }, spec: &command.InputSpec{ - Inputs: []string{"fooDirBlob", "fooDir"}, + Inputs: []string{"fooDirBlob", "fooDir"}, + InputNodeProperties: map[string]*repb.NodeProperties{"fooDir/foo": fooProperties}, }, rootDir: &repb.Directory{ Directories: []*repb.DirectoryNode{{Name: "fooDir", Digest: fooDirDgPb}}, @@ -1010,6 +1029,7 @@ func TestComputeMerkleTree(t *testing.T) { InputExclusions: []*command.InputExclusion{ &command.InputExclusion{Regex: `txt$`, Type: command.FileInputType}, }, + InputNodeProperties: map[string]*repb.NodeProperties{"fooDir/foo": fooProperties}, }, rootDir: &repb.Directory{Directories: []*repb.DirectoryNode{ {Name: "barDir", Digest: barDirDgPb}, @@ -1096,6 +1116,7 @@ func TestComputeMerkleTree(t *testing.T) { &command.VirtualInput{Path: "fooDir/foo", Contents: fooBlob, IsExecutable: true}, &command.VirtualInput{Path: "barDir/bar", Contents: barBlob}, }, + InputNodeProperties: map[string]*repb.NodeProperties{"fooDir/foo": fooProperties}, }, rootDir: &repb.Directory{Directories: []*repb.DirectoryNode{ {Name: "barDir", Digest: barDirDgPb}, @@ -1120,6 +1141,7 @@ func TestComputeMerkleTree(t *testing.T) { &command.VirtualInput{Path: "fooDir/foo", Contents: barBlob, IsExecutable: true}, &command.VirtualInput{Path: "barDir/bar", IsEmptyDirectory: true}, }, + InputNodeProperties: map[string]*repb.NodeProperties{"fooDir/foo": fooProperties}, }, rootDir: &repb.Directory{Directories: []*repb.DirectoryNode{ {Name: "barDir", Digest: barDirDgPb}, @@ -1149,6 +1171,7 @@ func TestComputeMerkleTree(t *testing.T) { VirtualInputs: []*command.VirtualInput{ &command.VirtualInput{Path: "barDir", IsEmptyDirectory: true}, }, + InputNodeProperties: map[string]*repb.NodeProperties{"fooDir/foo": fooProperties}, }, rootDir: &repb.Directory{Directories: []*repb.DirectoryNode{ {Name: "barDir", Digest: barDirDgPb}, @@ -1178,6 +1201,7 @@ func TestComputeMerkleTree(t *testing.T) { VirtualInputs: []*command.VirtualInput{ &command.VirtualInput{Path: "bar/baz", IsEmptyDirectory: true}, }, + InputNodeProperties: map[string]*repb.NodeProperties{"fooDir/foo": fooProperties}, }, rootDir: &repb.Directory{ Directories: []*repb.DirectoryNode{ @@ -1210,6 +1234,7 @@ func TestComputeMerkleTree(t *testing.T) { &command.VirtualInput{Path: "bar/baz", IsEmptyDirectory: true}, &command.VirtualInput{Path: "bar", IsEmptyDirectory: true}, }, + InputNodeProperties: map[string]*repb.NodeProperties{"fooDir/foo": fooProperties}, }, rootDir: &repb.Directory{ Directories: []*repb.DirectoryNode{ @@ -1237,6 +1262,7 @@ func TestComputeMerkleTree(t *testing.T) { &command.VirtualInput{Path: "//fooDir/../fooDir/foo", Contents: fooBlob, IsExecutable: true}, &command.VirtualInput{Path: "barDir///bar", Contents: barBlob}, }, + InputNodeProperties: map[string]*repb.NodeProperties{"fooDir/foo": fooProperties}, }, rootDir: &repb.Directory{Directories: []*repb.DirectoryNode{ {Name: "barDir", Digest: barDirDgPb}, @@ -1271,6 +1297,11 @@ func TestComputeMerkleTree(t *testing.T) { }, spec: &command.InputSpec{ Inputs: []string{"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l"}, + InputNodeProperties: map[string]*repb.NodeProperties{ + "g/foo": fooProperties, + "h/foo": fooProperties, + "i/foo": fooProperties, + }, }, rootDir: &repb.Directory{ Files: []*repb.FileNode{ @@ -1552,6 +1583,7 @@ func TestComputeOutputsToUploadFiles(t *testing.T) { input []*inputPath wd string paths []string + nodeProperties map[string]*repb.NodeProperties wantResult *repb.ActionResult wantBlobs [][]byte wantCacheCalls map[string]int @@ -1566,10 +1598,11 @@ func TestComputeOutputsToUploadFiles(t *testing.T) { input: []*inputPath{ {path: "foo", fileContents: fooBlob, isExecutable: true}, }, - paths: []string{"foo", "bar"}, - wantBlobs: [][]byte{fooBlob}, + paths: []string{"foo", "bar"}, + nodeProperties: map[string]*repb.NodeProperties{"foo": fooProperties}, + wantBlobs: [][]byte{fooBlob}, wantResult: &repb.ActionResult{ - OutputFiles: []*repb.OutputFile{&repb.OutputFile{Path: "foo", Digest: fooDgPb, IsExecutable: true}}, + OutputFiles: []*repb.OutputFile{&repb.OutputFile{Path: "foo", Digest: fooDgPb, IsExecutable: true, NodeProperties: fooProperties}}, }, wantCacheCalls: map[string]int{ "bar": 1, @@ -1582,12 +1615,13 @@ func TestComputeOutputsToUploadFiles(t *testing.T) { {path: "foo", fileContents: fooBlob, isExecutable: true}, {path: "bar", fileContents: barBlob}, }, - paths: []string{"foo", "bar"}, - wantBlobs: [][]byte{fooBlob, barBlob}, + paths: []string{"foo", "bar"}, + nodeProperties: map[string]*repb.NodeProperties{"foo": fooProperties}, + wantBlobs: [][]byte{fooBlob, barBlob}, wantResult: &repb.ActionResult{ OutputFiles: []*repb.OutputFile{ // Note the outputs are not sorted. - &repb.OutputFile{Path: "foo", Digest: fooDgPb, IsExecutable: true}, + &repb.OutputFile{Path: "foo", Digest: fooDgPb, IsExecutable: true, NodeProperties: fooProperties}, &repb.OutputFile{Path: "bar", Digest: barDgPb}, }, }, @@ -1602,13 +1636,14 @@ func TestComputeOutputsToUploadFiles(t *testing.T) { {path: "wd/foo", fileContents: fooBlob, isExecutable: true}, {path: "bar", fileContents: barBlob}, }, - paths: []string{"foo", "../bar"}, - wd: "wd", - wantBlobs: [][]byte{fooBlob, barBlob}, + paths: []string{"foo", "../bar"}, + nodeProperties: map[string]*repb.NodeProperties{"foo": fooProperties}, + wd: "wd", + wantBlobs: [][]byte{fooBlob, barBlob}, wantResult: &repb.ActionResult{ OutputFiles: []*repb.OutputFile{ // Note the outputs are not sorted. - &repb.OutputFile{Path: "foo", Digest: fooDgPb, IsExecutable: true}, + &repb.OutputFile{Path: "foo", Digest: fooDgPb, IsExecutable: true, NodeProperties: fooProperties}, &repb.OutputFile{Path: "../bar", Digest: barDgPb}, }, }, @@ -1640,12 +1675,13 @@ func TestComputeOutputsToUploadFiles(t *testing.T) { {path: "foo", fileContents: fooBlob, isExecutable: true}, {path: "bar", fileContents: fooBlob}, }, - paths: []string{"foo", "bar"}, - wantBlobs: [][]byte{fooBlob}, + paths: []string{"foo", "bar"}, + nodeProperties: map[string]*repb.NodeProperties{"foo": fooProperties}, + wantBlobs: [][]byte{fooBlob}, wantResult: &repb.ActionResult{ OutputFiles: []*repb.OutputFile{ // Note the outputs are not sorted. - &repb.OutputFile{Path: "foo", Digest: fooDgPb, IsExecutable: true}, + &repb.OutputFile{Path: "foo", Digest: fooDgPb, IsExecutable: true, NodeProperties: fooProperties}, &repb.OutputFile{Path: "bar", Digest: fooDgPb}, }, }, @@ -1673,7 +1709,7 @@ func TestComputeOutputsToUploadFiles(t *testing.T) { e, cleanup := fakes.NewTestEnv(t) defer cleanup() - inputs, gotResult, err := e.Client.GrpcClient.ComputeOutputsToUpload(root, tc.wd, tc.paths, cache, command.UnspecifiedSymlinkBehavior) + inputs, gotResult, err := e.Client.GrpcClient.ComputeOutputsToUpload(root, tc.wd, tc.paths, cache, command.UnspecifiedSymlinkBehavior, tc.nodeProperties) if err != nil { t.Errorf("ComputeOutputsToUpload(...) = gave error %v, want success", err) } @@ -1739,8 +1775,9 @@ func TestComputeOutputsToUploadDirectories(t *testing.T) { dirBDg := digest.NewFromBlob(dirBBlob) tests := []struct { - desc string - input []*inputPath + desc string + input []*inputPath + nodeProperties map[string]*repb.NodeProperties // The blobs are everything else outside of the Tree proto itself. wantBlobs [][]byte wantTreeRoot *repb.Directory @@ -1753,8 +1790,9 @@ func TestComputeOutputsToUploadDirectories(t *testing.T) { {path: "a/b/fooDir/foo", fileContents: fooBlob, isExecutable: true}, {path: "a/b/fooDir/bar", fileContents: barBlob}, }, - wantBlobs: [][]byte{fooBlob, barBlob}, - wantTreeRoot: foobarDir, + nodeProperties: map[string]*repb.NodeProperties{"foo": fooProperties}, + wantBlobs: [][]byte{fooBlob, barBlob}, + wantTreeRoot: foobarDir, wantCacheCalls: map[string]int{ "a/b/fooDir": 2, "a/b/fooDir/bar": 1, @@ -1767,10 +1805,11 @@ func TestComputeOutputsToUploadDirectories(t *testing.T) { {path: "a/b/fooDir/foo", fileContents: fooBlob, isExecutable: true}, {path: "a/b/fooDir/bar", fileContents: fooBlob, isExecutable: true}, }, - wantBlobs: [][]byte{fooBlob, fooBlob}, + nodeProperties: map[string]*repb.NodeProperties{"foo": fooProperties}, + wantBlobs: [][]byte{fooBlob, fooBlob}, wantTreeRoot: &repb.Directory{Files: []*repb.FileNode{ {Name: "bar", Digest: fooDgPb, IsExecutable: true}, - {Name: "foo", Digest: fooDgPb, IsExecutable: true}, + {Name: "foo", Digest: fooDgPb, IsExecutable: true, NodeProperties: fooProperties}, }}, wantCacheCalls: map[string]int{ "a/b/fooDir": 2, @@ -1784,7 +1823,8 @@ func TestComputeOutputsToUploadDirectories(t *testing.T) { {path: "a/b/fooDir/dir1/foo", fileContents: fooBlob, isExecutable: true}, {path: "a/b/fooDir/dir2/foo", fileContents: fooBlob, isExecutable: true}, }, - wantBlobs: [][]byte{fooBlob, fooDirBlob}, + nodeProperties: map[string]*repb.NodeProperties{"dir1/foo": fooProperties, "dir2/foo": fooProperties}, + wantBlobs: [][]byte{fooBlob, fooDirBlob}, wantTreeRoot: &repb.Directory{Directories: []*repb.DirectoryNode{ {Name: "dir1", Digest: fooDirDgPb}, {Name: "dir2", Digest: fooDirDgPb}, @@ -1807,7 +1847,8 @@ func TestComputeOutputsToUploadDirectories(t *testing.T) { {path: "a/b/fooDir/dirB/dirE/foo", fileContents: fooBlob, isExecutable: true}, {path: "a/b/fooDir/dirB/dirE/bar", fileContents: barBlob}, }, - wantBlobs: [][]byte{fooBlob, barBlob, fooDirBlob, barDirBlob, dirABlob, dirBBlob, bazDirBlob, bazBlob, foobarDirBlob}, + nodeProperties: map[string]*repb.NodeProperties{"dirA/dirF/foo": fooProperties, "dirB/dirE/foo": fooProperties}, + wantBlobs: [][]byte{fooBlob, barBlob, fooDirBlob, barDirBlob, dirABlob, dirBBlob, bazDirBlob, bazBlob, foobarDirBlob}, wantTreeRoot: &repb.Directory{Directories: []*repb.DirectoryNode{ {Name: "dirA", Digest: dirADg.ToProto()}, {Name: "dirB", Digest: dirBDg.ToProto()}, @@ -1847,7 +1888,7 @@ func TestComputeOutputsToUploadDirectories(t *testing.T) { e, cleanup := fakes.NewTestEnv(t) defer cleanup() - inputs, gotResult, err := e.Client.GrpcClient.ComputeOutputsToUpload(root, "", []string{"a/b/fooDir"}, cache, command.UnspecifiedSymlinkBehavior) + inputs, gotResult, err := e.Client.GrpcClient.ComputeOutputsToUpload(root, "", []string{"a/b/fooDir"}, cache, command.UnspecifiedSymlinkBehavior, tc.nodeProperties) if err != nil { t.Fatalf("ComputeOutputsToUpload(...) = gave error %v, want success", err) } @@ -1877,7 +1918,7 @@ func TestComputeOutputsToUploadDirectories(t *testing.T) { digests[gotResult.OutputDirectories[0].TreeDigest.Hash] = true for i := 0; i < 5; i++ { - _, gotResult, err = e.Client.GrpcClient.ComputeOutputsToUpload(root, "", []string{"a/b/fooDir"}, cache, command.UnspecifiedSymlinkBehavior) + _, gotResult, err = e.Client.GrpcClient.ComputeOutputsToUpload(root, "", []string{"a/b/fooDir"}, cache, command.UnspecifiedSymlinkBehavior, tc.nodeProperties) if err != nil { t.Fatalf("ComputeOutputsToUpload(...) = gave error %v, want success", err) } diff --git a/go/pkg/command/command.go b/go/pkg/command/command.go index 9a776cc2a..bb097a0bd 100644 --- a/go/pkg/command/command.go +++ b/go/pkg/command/command.go @@ -112,6 +112,9 @@ type InputSpec struct { // SymlinkBehavior represents the way symlinks will be handled. SymlinkBehavior SymlinkBehaviorType + + // Node properties of inputs. + InputNodeProperties map[string]*repb.NodeProperties } // String returns the string representation of the VirtualInput. @@ -585,6 +588,35 @@ func (c *Command) ToREProto(useOutputPathsField bool) *repb.Command { return cmdPb } +func FromREProto(cmdPb *repb.Command) *Command { + cmd := &Command{ + InputSpec: &InputSpec{ + EnvironmentVariables: make(map[string]string), + InputNodeProperties: make(map[string]*repb.NodeProperties), + }, + Identifiers: &Identifiers{}, + WorkingDir: cmdPb.WorkingDirectory, + OutputFiles: cmdPb.OutputFiles, + OutputDirs: cmdPb.OutputDirectories, + Platform: make(map[string]string), + Args: cmdPb.Arguments, + } + + // In v2.1 of the RE API the `output_{files, directories}` fields were + // replaced by a single field: `output_paths`. + if len(cmdPb.OutputPaths) > 0 { + cmd.OutputFiles = cmdPb.OutputPaths + cmd.OutputDirs = nil + } + for _, ev := range cmdPb.EnvironmentVariables { + cmd.InputSpec.EnvironmentVariables[ev.Name] = ev.Value + } + for _, pt := range cmdPb.GetPlatform().GetProperties() { + cmd.Platform[pt.Name] = pt.Value + } + return cmd +} + // FromProto parses a Command struct from a proto message. func FromProto(p *cpb.Command) *Command { ids := &Identifiers{ @@ -635,6 +667,7 @@ func inputSpecFromProto(is *cpb.InputSpec) *InputSpec { InputExclusions: excl, EnvironmentVariables: is.GetEnvironmentVariables(), SymlinkBehavior: symlinkBehaviorFromProto(is.GetSymlinkBehavior()), + InputNodeProperties: is.GetInputNodeProperties(), } } @@ -663,6 +696,7 @@ func inputSpecToProto(is *InputSpec) *cpb.InputSpec { ExcludeInputs: excl, EnvironmentVariables: is.EnvironmentVariables, SymlinkBehavior: symlinkBehaviorToProto(is.SymlinkBehavior), + InputNodeProperties: is.InputNodeProperties, } } diff --git a/go/pkg/rexec/rexec.go b/go/pkg/rexec/rexec.go index 1a500fbca..97dedd5af 100644 --- a/go/pkg/rexec/rexec.go +++ b/go/pkg/rexec/rexec.go @@ -277,7 +277,7 @@ func (ec *Context) UpdateCachedResult() { if !ec.client.GrpcClient.LegacyExecRootRelativeOutputs { wd = ec.cmd.WorkingDir } - blobs, resPb, err := ec.client.GrpcClient.ComputeOutputsToUpload(ec.cmd.ExecRoot, wd, outPaths, ec.client.FileMetadataCache, ec.cmd.InputSpec.SymlinkBehavior) + blobs, resPb, err := ec.client.GrpcClient.ComputeOutputsToUpload(ec.cmd.ExecRoot, wd, outPaths, ec.client.FileMetadataCache, ec.cmd.InputSpec.SymlinkBehavior, ec.cmd.InputSpec.InputNodeProperties) if err != nil { ec.Result = command.NewLocalErrorResult(err) return diff --git a/go/pkg/tool/BUILD.bazel b/go/pkg/tool/BUILD.bazel index 887a25a38..e6a2e06fe 100644 --- a/go/pkg/tool/BUILD.bazel +++ b/go/pkg/tool/BUILD.bazel @@ -6,6 +6,7 @@ go_library( importpath = "github.com/bazelbuild/remote-apis-sdks/go/pkg/tool", visibility = ["//visibility:public"], deps = [ + "//go/api/command", "//go/pkg/cas", "//go/pkg/client", "//go/pkg/command", @@ -32,6 +33,7 @@ go_test( "//go/pkg/digest", "//go/pkg/fakes", "//go/pkg/outerr", + "@com_github_bazelbuild_remote_apis//build/bazel/remote/execution/v2:remote_execution_go_proto", "@com_github_google_go_cmp//cmp:go_default_library", "@org_golang_google_protobuf//encoding/prototext:go_default_library", ], diff --git a/go/pkg/tool/tool.go b/go/pkg/tool/tool.go index bea3f7042..cbb046463 100644 --- a/go/pkg/tool/tool.go +++ b/go/pkg/tool/tool.go @@ -26,6 +26,8 @@ import ( "github.com/bazelbuild/remote-apis-sdks/go/pkg/outerr" "github.com/bazelbuild/remote-apis-sdks/go/pkg/rexec" "github.com/bazelbuild/remote-apis-sdks/go/pkg/uploadinfo" + + cpb "github.com/bazelbuild/remote-apis-sdks/go/api/command" repb "github.com/bazelbuild/remote-apis/build/bazel/remote/execution/v2" ) @@ -79,7 +81,7 @@ func (c *Client) CheckDeterminism(ctx context.Context, actionDigest, actionRoot return nil } -func (c *Client) prepCommand(ctx context.Context, client *rexec.Client, actionDigest, inputRoot string) (*command.Command, error) { +func (c *Client) prepCommand(ctx context.Context, client *rexec.Client, actionDigest, actionRoot string) (*command.Command, error) { acDg, err := digest.NewFromString(actionDigest) if err != nil { return nil, err @@ -99,18 +101,44 @@ func (c *Client) prepCommand(ctx context.Context, client *rexec.Client, actionDi if _, err := c.GrpcClient.ReadProto(ctx, cmdDg, commandProto); err != nil { return nil, err } - if inputRoot == "" { + var inputRoot string + var nodeProperties map[string]*repb.NodeProperties + if actionRoot == "" { curTime := time.Now().Format(time.RFC3339) - inputRoot = filepath.Join(os.TempDir(), acDg.Hash+"_"+curTime) + actionRoot = filepath.Join(os.TempDir(), acDg.Hash+"_"+curTime) + inputRoot = filepath.Join(actionRoot, "input") dg, err := digest.NewFromProto(actionProto.GetInputRootDigest()) if err != nil { return nil, err } log.Infof("Fetching input tree from input root digest %s into %s", dg, inputRoot) - _, _, err = c.GrpcClient.DownloadDirectory(ctx, dg, inputRoot, client.FileMetadataCache) + ts, _, err := c.GrpcClient.DownloadDirectory(ctx, dg, inputRoot, client.FileMetadataCache) if err != nil { return nil, err } + nodeProperties = make(map[string]*repb.NodeProperties) + for path, t := range ts { + if t.NodeProperties != nil { + nodeProperties[path] = t.NodeProperties + } + } + } else { + inputRoot = filepath.Join(actionRoot, "input") + npPath := filepath.Join(actionRoot, "input_node_properties.textproto") + if _, err := os.Stat(npPath); err == nil { + // Read node properties from file. + inTxt, err := os.ReadFile(npPath) + if err != nil { + return nil, err + } + ipb := &cpb.InputSpec{} + if err := prototext.Unmarshal(inTxt, ipb); err != nil { + return nil, err + } + nodeProperties = ipb.InputNodeProperties + } else if !errors.Is(err, os.ErrNotExist) { + return nil, err + } } contents, err := os.ReadDir(inputRoot) if err != nil { @@ -121,8 +149,9 @@ func (c *Client) prepCommand(ctx context.Context, client *rexec.Client, actionDi inputPaths = append(inputPaths, f.Name()) } // Construct Command object. - cmd := commandFromREProto(commandProto) + cmd := command.FromREProto(commandProto) cmd.InputSpec.Inputs = inputPaths + cmd.InputSpec.InputNodeProperties = nodeProperties cmd.ExecRoot = inputRoot if actionProto.Timeout != nil { cmd.Timeout = actionProto.Timeout.AsDuration() @@ -130,28 +159,6 @@ func (c *Client) prepCommand(ctx context.Context, client *rexec.Client, actionDi return cmd, nil } -func commandFromREProto(cmdPb *repb.Command) *command.Command { - cmd := &command.Command{ - InputSpec: &command.InputSpec{ - EnvironmentVariables: make(map[string]string), - }, - Identifiers: &command.Identifiers{}, - WorkingDir: cmdPb.WorkingDirectory, - OutputFiles: cmdPb.OutputFiles, - OutputDirs: cmdPb.OutputDirectories, - Platform: make(map[string]string), - Args: cmdPb.Arguments, - } - - for _, ev := range cmdPb.EnvironmentVariables { - cmd.InputSpec.EnvironmentVariables[ev.Name] = ev.Value - } - for _, pt := range cmdPb.GetPlatform().GetProperties() { - cmd.Platform[pt.Name] = pt.Value - } - return cmd -} - // DownloadActionResult downloads the action result of the given action digest // if it exists in the remote cache. func (c *Client) DownloadActionResult(ctx context.Context, actionDigest, pathPrefix string) error { @@ -173,7 +180,7 @@ func (c *Client) DownloadActionResult(ctx context.Context, actionDigest, pathPre return err } // Construct Command object. - cmd := commandFromREProto(commandProto) + cmd := command.FromREProto(commandProto) resPb, err := c.getActionResult(ctx, actionDigest) if err != nil { @@ -327,6 +334,9 @@ func (c *Client) writeProto(m proto.Message, baseName string) error { // 1. ac.textproto: the action proto file in text format. // 2. cmd.textproto: the command proto file in text format. // 3. input/: the input tree root directory with all files under it. +// 4. input_node_properties.txtproto: all the NodeProperties defined on the +// input tree, as an InputSpec proto file in text format. Will be omitted +// if no NodeProperties are defined. func (c *Client) DownloadAction(ctx context.Context, actionDigest, outputPath string) error { acDg, err := digest.NewFromString(actionDigest) if err != nil { @@ -362,8 +372,20 @@ func (c *Client) DownloadAction(ctx context.Context, actionDigest, outputPath st if err != nil { return err } - _, _, err = c.GrpcClient.DownloadDirectory(ctx, rDg, rootPath, filemetadata.NewNoopCache()) - return err + is := &cpb.InputSpec{InputNodeProperties: make(map[string]*repb.NodeProperties)} + ts, _, err := c.GrpcClient.DownloadDirectory(ctx, rDg, rootPath, filemetadata.NewNoopCache()) + if err != nil { + return err + } + for path, t := range ts { + if t.NodeProperties != nil { + is.InputNodeProperties[path] = t.NodeProperties + } + } + if len(is.InputNodeProperties) == 0 { + return nil + } + return c.writeProto(is, filepath.Join(outputPath, "input_node_properties.textproto")) } func (c *Client) prepProtos(ctx context.Context, actionRoot string) (string, error) { @@ -415,6 +437,7 @@ func (c *Client) prepProtos(ctx context.Context, actionRoot string) (string, err // // > ac.textproto (Action text proto) // > cmd.textproto (Command text proto) +// > input_node_properties.textproto (InputSpec text proto, optional) // > input (Input root) // > inputs... func (c *Client) ExecuteAction(ctx context.Context, actionDigest, actionRoot, outDir string, oe outerr.OutErr) (*command.Metadata, error) { @@ -423,15 +446,13 @@ func (c *Client) ExecuteAction(ctx context.Context, actionDigest, actionRoot, ou FileMetadataCache: fmc, GrpcClient: c.GrpcClient, } - inputRoot := "" if actionRoot != "" { var err error if actionDigest, err = c.prepProtos(ctx, actionRoot); err != nil { return nil, err } - inputRoot = filepath.Join(actionRoot, "input") } - cmd, err := c.prepCommand(ctx, client, actionDigest, inputRoot) + cmd, err := c.prepCommand(ctx, client, actionDigest, actionRoot) if err != nil { return nil, err } @@ -449,6 +470,9 @@ func (c *Client) ExecuteAction(ctx context.Context, actionDigest, actionRoot, ou fmt.Printf("Stderr digest: %v\n", ec.Metadata.StderrDigest.String()) fmt.Printf("Number of Input Files: %v\n", ec.Metadata.InputFiles) fmt.Printf("Number of Input Dirs: %v\n", ec.Metadata.InputDirectories) + if len(cmd.InputSpec.InputNodeProperties) != 0 { + fmt.Printf("Number of Input Node Properties: %d\n", len(cmd.InputSpec.InputNodeProperties)) + } fmt.Printf("Number of Output Files: %v\n", ec.Metadata.OutputFiles) fmt.Printf("Number of Output Directories: %v\n", ec.Metadata.OutputDirectories) switch ec.Result.Status { @@ -643,12 +667,16 @@ func (c *Client) flattenTree(ctx context.Context, t *repb.Tree) (string, []strin sort.Strings(paths) for _, path := range paths { output := outputs[path] + var np string + if output.NodeProperties != nil { + np = fmt.Sprintf(" [Node properties: %v]", prototext.MarshalOptions{Multiline: false}.Format(output.NodeProperties)) + } if output.IsEmptyDirectory { - res.WriteString(fmt.Sprintf("%v: [Directory digest: %v]\n", path, output.Digest)) + res.WriteString(fmt.Sprintf("%v: [Directory digest: %v]%s\n", path, output.Digest, np)) } else if output.SymlinkTarget != "" { - res.WriteString(fmt.Sprintf("%v: [Symlink digest: %v, Symlink Target: %v]\n", path, output.Digest, output.SymlinkTarget)) + res.WriteString(fmt.Sprintf("%v: [Symlink digest: %v, Symlink Target: %v]%s\n", path, output.Digest, output.SymlinkTarget, np)) } else { - res.WriteString(fmt.Sprintf("%v: [File digest: %v]\n", path, output.Digest)) + res.WriteString(fmt.Sprintf("%v: [File digest: %v]%s\n", path, output.Digest, np)) } } return res.String(), paths, nil diff --git a/go/pkg/tool/tool_test.go b/go/pkg/tool/tool_test.go index 4f8ce9b39..caa6c2af9 100644 --- a/go/pkg/tool/tool_test.go +++ b/go/pkg/tool/tool_test.go @@ -14,9 +14,12 @@ import ( "github.com/google/go-cmp/cmp" "google.golang.org/protobuf/encoding/prototext" + cpb "github.com/bazelbuild/remote-apis-sdks/go/api/command" repb "github.com/bazelbuild/remote-apis/build/bazel/remote/execution/v2" ) +var fooProperties = &repb.NodeProperties{Properties: []*repb.NodeProperty{{Name: "fooName", Value: "fooValue"}}} + func TestTool_DownloadActionResult(t *testing.T) { e, cleanup := fakes.NewTestEnv(t) defer cleanup() @@ -62,14 +65,16 @@ func TestTool_ShowAction(t *testing.T) { InputSpec: &command.InputSpec{ Inputs: []string{ "a/b/input.txt", + "a/b/input2.txt", }, + InputNodeProperties: map[string]*repb.NodeProperties{"a/b/input2.txt": fooProperties}, }, OutputFiles: []string{"a/b/out"}, } opt := command.DefaultExecutionOptions() _, acDg, _, _ := e.Set(cmd, opt, &command.Result{Status: command.CacheHitResultStatus}, &fakes.OutputFile{Path: "a/b/out", Contents: "output"}, - fakes.StdOut("stdout"), fakes.StdErr("stderr"), &fakes.InputFile{Path: "a/b/input.txt", Contents: "input"}) + fakes.StdOut("stdout"), fakes.StdErr("stderr"), &fakes.InputFile{Path: "a/b/input.txt", Contents: "input"}, &fakes.InputFile{Path: "a/b/input2.txt", Contents: "input2"}) toolClient := &Client{GrpcClient: e.Client.GrpcClient} got, err := toolClient.ShowAction(context.Background(), acDg.String()) @@ -86,8 +91,9 @@ Platform Inputs ====== -[Root directory digest: 1b1d5b9e3eb97866805deca901299fcdde98b5747e147f35187e12c16e120186/75] +[Root directory digest: 456e94a43b31b158fa7b3fe8d3a8cd6f0b66ef8a6a05ab8350e03df83b9740b6/75] a/b/input.txt: [File digest: c96c6d5be8d08a12e7b5cdc1b207fa6b2430974c86803d8891675e76fd992c20/5] +a/b/input2.txt: [File digest: 124d8541ff3d7a18b95432bdfbecd86816b86c8265bff44ef629765afb25f06b/6] [Node properties: properties:{name:"fooName" value:"fooValue"}] ------------------------------------------------------------------------ Action Result @@ -139,7 +145,7 @@ func TestTool_DownloadAction(t *testing.T) { cmd := &command.Command{ Args: []string{"foo", "bar", "baz"}, ExecRoot: e.ExecRoot, - InputSpec: &command.InputSpec{Inputs: []string{"i1", "a/b/i2"}}, + InputSpec: &command.InputSpec{Inputs: []string{"i1", "a/b/i2"}, InputNodeProperties: map[string]*repb.NodeProperties{"i1": fooProperties}}, OutputFiles: []string{"a/b/out"}, } _, acDg, _, _ := e.Set(cmd, command.DefaultExecutionOptions(), &command.Result{Status: command.SuccessResultStatus}, &fakes.InputFile{Path: "i1", Contents: "i1"}, &fakes.InputFile{Path: "a/b/i2", Contents: "i2"}) @@ -157,8 +163,9 @@ func TestTool_DownloadAction(t *testing.T) { } acPb := &repb.Action{ CommandDigest: digest.TestNewFromMessage(reCmdPb).ToProto(), - InputRootDigest: &repb.Digest{Hash: "2f33e7247d11728cfc1515b378040ecf3bd8be4eb4330ca7b6023b23c43b348b", SizeBytes: 153}, + InputRootDigest: &repb.Digest{Hash: "01dc5b6fa6d16d30bf80a7d88ff58f13fe801c4060b9782253b94e793444df05", SizeBytes: 176}, } + ipPb := &cpb.InputSpec{InputNodeProperties: cmd.InputSpec.InputNodeProperties} expectedContents := []struct { path string contents string @@ -171,6 +178,10 @@ func TestTool_DownloadAction(t *testing.T) { path: "cmd.textproto", contents: prototext.Format(reCmdPb), }, + { + path: "input_node_properties.textproto", + contents: prototext.Format(ipPb), + }, { path: "input/i1", contents: "i1", @@ -198,7 +209,7 @@ func TestTool_ExecuteAction(t *testing.T) { cmd := &command.Command{ Args: []string{"foo", "bar", "baz"}, ExecRoot: e.ExecRoot, - InputSpec: &command.InputSpec{Inputs: []string{"i1", "i2"}}, + InputSpec: &command.InputSpec{Inputs: []string{"i1", "i2"}, InputNodeProperties: map[string]*repb.NodeProperties{"i1": fooProperties}}, OutputFiles: []string{"a/b/out"}, } opt := &command.ExecutionOptions{AcceptCached: false, DownloadOutputs: true, DownloadOutErr: true} @@ -254,6 +265,36 @@ func TestTool_ExecuteAction(t *testing.T) { if string(oe.Stdout()) != "stdout2" { t.Errorf("Incorrect stdout %v, expected \"stdout2\"", oe.Stdout()) } + // Now execute again without node properties. + fp = filepath.Join(tmpDir, "input_node_properties.textproto") + if err := os.Remove(fp); err != nil { + t.Fatalf("Unable to remove %v: %v", fp, err) + } + cmd.InputSpec.InputNodeProperties = nil + _, acDg3, _, _ := e.Set(cmd, opt, &command.Result{Status: command.SuccessResultStatus}, &fakes.OutputFile{Path: "a/b/out", Contents: "out3"}, + fakes.StdOut("stdout3"), fakes.StdErr("stderr3")) + if diff := cmp.Diff(acDg2, acDg3); diff == "" { + t.Errorf("Expected action digests to be different when input node properties change, got: %v\n", acDg2) + } + oe = outerr.NewRecordingOutErr() + if _, err := client.ExecuteAction(context.Background(), acDg3.String(), "", tmpDir, oe); err != nil { + t.Errorf("error executeAction: %v", err) + } + + fp = filepath.Join(tmpDir, "a/b/out") + c, err = os.ReadFile(fp) + if err != nil { + t.Fatalf("Unable to read downloaded output %v: %v", fp, err) + } + if string(c) != "out3" { + t.Fatalf("Incorrect content in downloaded file %v, want \"out3\", got %s", fp, c) + } + if string(oe.Stderr()) != "stderr3" { + t.Errorf("Incorrect stderr %v, expected \"stderr3\"", oe.Stderr()) + } + if string(oe.Stdout()) != "stdout3" { + t.Errorf("Incorrect stdout %v, expected \"stdout3\"", oe.Stdout()) + } } func TestTool_ExecuteActionFromRoot(t *testing.T) { @@ -262,7 +303,7 @@ func TestTool_ExecuteActionFromRoot(t *testing.T) { cmd := &command.Command{ Args: []string{"foo", "bar", "baz"}, ExecRoot: e.ExecRoot, - InputSpec: &command.InputSpec{Inputs: []string{"i1", "i2"}}, + InputSpec: &command.InputSpec{Inputs: []string{"i1", "i2"}, InputNodeProperties: map[string]*repb.NodeProperties{"i1": fooProperties}}, OutputFiles: []string{"a/b/out"}, } // Create files necessary for the fake @@ -286,6 +327,12 @@ func TestTool_ExecuteActionFromRoot(t *testing.T) { if err := os.WriteFile(filepath.Join(e.ExecRoot, "input", "i2"), []byte("i2"), 0644); err != nil { t.Fatalf("failed creating input file: %v", err) } + ipPb := &cpb.InputSpec{ + InputNodeProperties: map[string]*repb.NodeProperties{"i1": fooProperties}, + } + if err := os.WriteFile(filepath.Join(e.ExecRoot, "input_node_properties.textproto"), []byte(prototext.Format(ipPb)), 0644); err != nil { + t.Fatalf("failed creating input node properties file: %v", err) + } reCmdPb := &repb.Command{ Arguments: []string{"foo", "bar", "baz"}, OutputFiles: []string{"a/b/out"}, From eb57ce329ed5ad10c7d10f29c7b0a2389b364b8e Mon Sep 17 00:00:00 2001 From: Ola Rozenfeld Date: Tue, 4 Jul 2023 22:22:54 -0400 Subject: [PATCH 2/5] fixing test flakiness --- go/pkg/tool/tool_test.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/go/pkg/tool/tool_test.go b/go/pkg/tool/tool_test.go index caa6c2af9..a8b93ad7f 100644 --- a/go/pkg/tool/tool_test.go +++ b/go/pkg/tool/tool_test.go @@ -2,6 +2,7 @@ package tool import ( "context" + "fmt" "os" "path" "path/filepath" @@ -81,7 +82,7 @@ func TestTool_ShowAction(t *testing.T) { if err != nil { t.Fatalf("ShowAction(%v) failed: %v", acDg.String(), err) } - want := `Command + want := fmt.Sprintf(`Command ======= Command Digest: 76a608e419da9ed3673f59b8b903f21dbf7cc3178281029151a090cac02d9e4d/15 tool @@ -93,7 +94,7 @@ Inputs ====== [Root directory digest: 456e94a43b31b158fa7b3fe8d3a8cd6f0b66ef8a6a05ab8350e03df83b9740b6/75] a/b/input.txt: [File digest: c96c6d5be8d08a12e7b5cdc1b207fa6b2430974c86803d8891675e76fd992c20/5] -a/b/input2.txt: [File digest: 124d8541ff3d7a18b95432bdfbecd86816b86c8265bff44ef629765afb25f06b/6] [Node properties: properties:{name:"fooName" value:"fooValue"}] +a/b/input2.txt: [File digest: 124d8541ff3d7a18b95432bdfbecd86816b86c8265bff44ef629765afb25f06b/6] [Node properties: %s] ------------------------------------------------------------------------ Action Result @@ -108,7 +109,7 @@ a/b/out, digest: e0ee8bb50685e05fa0f47ed04203ae953fdfd055f5bd2892ea186504254f8c3 Output Files From Directories ============================= -` +`, prototext.MarshalOptions{Multiline: false}.Format(fooProperties)) if diff := cmp.Diff(want, got); diff != "" { t.Fatalf("ShowAction(%v) returned diff (-want +got): %v\n\ngot: %v\n\nwant: %v\n", acDg.String(), diff, got, want) } From 131ca1fd70a59825d54cfb70027561df1fcae86e Mon Sep 17 00:00:00 2001 From: Ola Rozenfeld Date: Tue, 4 Jul 2023 22:51:16 -0400 Subject: [PATCH 3/5] fixing lint errors (unwrapped errors) --- go/pkg/client/tree.go | 2 ++ go/pkg/tool/tool.go | 9 ++++----- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/go/pkg/client/tree.go b/go/pkg/client/tree.go index 201957f95..8ea958f2e 100644 --- a/go/pkg/client/tree.go +++ b/go/pkg/client/tree.go @@ -377,6 +377,7 @@ func packageTree(t *treeNode, stats *TreeStats) (root digest.Digest, blobs map[d sort.Slice(dir.Directories, func(i, j int) bool { return dir.Directories[i].Name < dir.Directories[j].Name }) for name, n := range t.leaves { + // A node can have exactly one of file/symlink/emptyDirectoryMarker. if n.file != nil { dg := n.file.ue.Digest dir.Files = append(dir.Files, &repb.FileNode{Name: name, Digest: dg.ToProto(), IsExecutable: n.file.isExecutable, NodeProperties: n.nodeProperties}) @@ -529,6 +530,7 @@ func packageDirectories(t *treeNode) (root *repb.Directory, files map[digest.Dig sort.Slice(root.Directories, func(i, j int) bool { return root.Directories[i].Name < root.Directories[j].Name }) for name, n := range t.leaves { + // A node can have exactly one of file/symlink/emptyDirectoryMarker. if n.file != nil { dg := n.file.ue.Digest root.Files = append(root.Files, &repb.FileNode{Name: name, Digest: dg.ToProto(), IsExecutable: n.file.isExecutable, NodeProperties: n.nodeProperties}) diff --git a/go/pkg/tool/tool.go b/go/pkg/tool/tool.go index cbb046463..ca9329d90 100644 --- a/go/pkg/tool/tool.go +++ b/go/pkg/tool/tool.go @@ -126,18 +126,17 @@ func (c *Client) prepCommand(ctx context.Context, client *rexec.Client, actionDi inputRoot = filepath.Join(actionRoot, "input") npPath := filepath.Join(actionRoot, "input_node_properties.textproto") if _, err := os.Stat(npPath); err == nil { - // Read node properties from file. inTxt, err := os.ReadFile(npPath) if err != nil { - return nil, err + return nil, fmt.Errorf("error reading input node properties from file: %v", err) } ipb := &cpb.InputSpec{} if err := prototext.Unmarshal(inTxt, ipb); err != nil { - return nil, err + return nil, fmt.Errorf("error unmarshalling input node properties from file %s: %v", npPath, err) } nodeProperties = ipb.InputNodeProperties } else if !errors.Is(err, os.ErrNotExist) { - return nil, err + return nil, fmt.Errorf("error accessing input node properties file: %v", err) } } contents, err := os.ReadDir(inputRoot) @@ -375,7 +374,7 @@ func (c *Client) DownloadAction(ctx context.Context, actionDigest, outputPath st is := &cpb.InputSpec{InputNodeProperties: make(map[string]*repb.NodeProperties)} ts, _, err := c.GrpcClient.DownloadDirectory(ctx, rDg, rootPath, filemetadata.NewNoopCache()) if err != nil { - return err + return fmt.Errorf("error fetching input tree: %v", err) } for path, t := range ts { if t.NodeProperties != nil { From 1ab18ae5006dc87b55c2a625d9ba7d299efbfef5 Mon Sep 17 00:00:00 2001 From: Ola Rozenfeld Date: Wed, 5 Jul 2023 18:08:08 -0400 Subject: [PATCH 4/5] Addressing comments --- go/pkg/tool/tool.go | 48 +++++++++++++++++++++++++-------------------- 1 file changed, 27 insertions(+), 21 deletions(-) diff --git a/go/pkg/tool/tool.go b/go/pkg/tool/tool.go index ca9329d90..ba00dbaf2 100644 --- a/go/pkg/tool/tool.go +++ b/go/pkg/tool/tool.go @@ -101,12 +101,14 @@ func (c *Client) prepCommand(ctx context.Context, client *rexec.Client, actionDi if _, err := c.GrpcClient.ReadProto(ctx, cmdDg, commandProto); err != nil { return nil, err } - var inputRoot string - var nodeProperties map[string]*repb.NodeProperties - if actionRoot == "" { + fetchInputs := actionRoot == "" + if fetchInputs { curTime := time.Now().Format(time.RFC3339) actionRoot = filepath.Join(os.TempDir(), acDg.Hash+"_"+curTime) - inputRoot = filepath.Join(actionRoot, "input") + } + inputRoot := filepath.Join(actionRoot, "input") + var nodeProperties map[string]*repb.NodeProperties + if fetchInputs { dg, err := digest.NewFromProto(actionProto.GetInputRootDigest()) if err != nil { return nil, err @@ -122,22 +124,8 @@ func (c *Client) prepCommand(ctx context.Context, client *rexec.Client, actionDi nodeProperties[path] = t.NodeProperties } } - } else { - inputRoot = filepath.Join(actionRoot, "input") - npPath := filepath.Join(actionRoot, "input_node_properties.textproto") - if _, err := os.Stat(npPath); err == nil { - inTxt, err := os.ReadFile(npPath) - if err != nil { - return nil, fmt.Errorf("error reading input node properties from file: %v", err) - } - ipb := &cpb.InputSpec{} - if err := prototext.Unmarshal(inTxt, ipb); err != nil { - return nil, fmt.Errorf("error unmarshalling input node properties from file %s: %v", npPath, err) - } - nodeProperties = ipb.InputNodeProperties - } else if !errors.Is(err, os.ErrNotExist) { - return nil, fmt.Errorf("error accessing input node properties file: %v", err) - } + } else if nodeProperties, err = readNodePropertiesFromFile(filepath.Join(actionRoot, "input_node_properties.textproto")); err != nil { + return nil, err } contents, err := os.ReadDir(inputRoot) if err != nil { @@ -158,6 +146,24 @@ func (c *Client) prepCommand(ctx context.Context, client *rexec.Client, actionDi return cmd, nil } +func readNodePropertiesFromFile(path string) (nps map[string]*repb.NodeProperties, err error) { + if _, err = os.Stat(path); err == nil { + inTxt, err := os.ReadFile(path) + if err != nil { + return nil, fmt.Errorf("error reading input node properties from file: %v", err) + } + ipb := &cpb.InputSpec{} + if err := prototext.Unmarshal(inTxt, ipb); err != nil { + return nil, fmt.Errorf("error unmarshalling input node properties from file %s: %v", path, err) + } + return ipb.InputNodeProperties, nil + } + if !errors.Is(err, os.ErrNotExist) { + return nil, fmt.Errorf("error accessing input node properties file: %v", err) + } + return nil, nil +} + // DownloadActionResult downloads the action result of the given action digest // if it exists in the remote cache. func (c *Client) DownloadActionResult(ctx context.Context, actionDigest, pathPrefix string) error { @@ -371,11 +377,11 @@ func (c *Client) DownloadAction(ctx context.Context, actionDigest, outputPath st if err != nil { return err } - is := &cpb.InputSpec{InputNodeProperties: make(map[string]*repb.NodeProperties)} ts, _, err := c.GrpcClient.DownloadDirectory(ctx, rDg, rootPath, filemetadata.NewNoopCache()) if err != nil { return fmt.Errorf("error fetching input tree: %v", err) } + is := &cpb.InputSpec{InputNodeProperties: make(map[string]*repb.NodeProperties)} for path, t := range ts { if t.NodeProperties != nil { is.InputNodeProperties[path] = t.NodeProperties From 9ef6dd4cfd35db722888c67955fd9d9d7807adea Mon Sep 17 00:00:00 2001 From: Ola Rozenfeld Date: Thu, 6 Jul 2023 12:13:39 -0400 Subject: [PATCH 5/5] addressing more comments --- go/pkg/tool/tool.go | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/go/pkg/tool/tool.go b/go/pkg/tool/tool.go index ba00dbaf2..d1fbd75c6 100644 --- a/go/pkg/tool/tool.go +++ b/go/pkg/tool/tool.go @@ -147,21 +147,21 @@ func (c *Client) prepCommand(ctx context.Context, client *rexec.Client, actionDi } func readNodePropertiesFromFile(path string) (nps map[string]*repb.NodeProperties, err error) { - if _, err = os.Stat(path); err == nil { - inTxt, err := os.ReadFile(path) - if err != nil { - return nil, fmt.Errorf("error reading input node properties from file: %v", err) - } - ipb := &cpb.InputSpec{} - if err := prototext.Unmarshal(inTxt, ipb); err != nil { - return nil, fmt.Errorf("error unmarshalling input node properties from file %s: %v", path, err) + if _, err = os.Stat(path); err != nil { + if !errors.Is(err, os.ErrNotExist) { + return nil, fmt.Errorf("error accessing input node properties file: %v", err) } - return ipb.InputNodeProperties, nil + return nil, nil + } + inTxt, err := os.ReadFile(path) + if err != nil { + return nil, fmt.Errorf("error reading input node properties from file: %v", err) } - if !errors.Is(err, os.ErrNotExist) { - return nil, fmt.Errorf("error accessing input node properties file: %v", err) + ipb := &cpb.InputSpec{} + if err := prototext.Unmarshal(inTxt, ipb); err != nil { + return nil, fmt.Errorf("error unmarshalling input node properties from file %s: %v", path, err) } - return nil, nil + return ipb.InputNodeProperties, nil } // DownloadActionResult downloads the action result of the given action digest