From 13e7f92c8c5a8ef9ccd8c36370ff1a04ab7bed7c Mon Sep 17 00:00:00 2001 From: Nils Wireklint Date: Mon, 4 Sep 2023 14:05:14 +0200 Subject: [PATCH] Allow runners to mount special filesystem in the input root This introduces a new runner decorator that can create mounts in the input root. This is useful for 'chroot' runners that have userland tools that require '/proc' or '/sys', which are created by the operating system. This is how to configure the runner: inputRootMounts: [{ mountpoint: 'proc', source: '/proc', filesystemType: 'proc', }, { mountpoint: 'sys', source: '/sys', filesystemType: 'sysfs', }], --- .bazelrc | 1 + cmd/bb_runner/main.go | 7 + cmd/bb_scheduler/main.go | 2 +- pkg/filesystem/lazy_directory.go | 18 ++ .../configuration/bb_runner/bb_runner.pb.go | 158 ++++++++++++++---- .../configuration/bb_runner/bb_runner.proto | 19 +++ pkg/runner/BUILD.bazel | 2 + pkg/runner/mounting_runner.go | 101 +++++++++++ 8 files changed, 277 insertions(+), 31 deletions(-) create mode 100644 pkg/runner/mounting_runner.go diff --git a/.bazelrc b/.bazelrc index 506f05c3..2a4739cd 100644 --- a/.bazelrc +++ b/.bazelrc @@ -1,2 +1,3 @@ +# Required for @com_google_absl and remoteapis that use it. build --host_cxxopt=-std=c++17 run --workspace_status_command="bash tools/workspace-status.sh" diff --git a/cmd/bb_runner/main.go b/cmd/bb_runner/main.go index 000ae003..6252e99b 100644 --- a/cmd/bb_runner/main.go +++ b/cmd/bb_runner/main.go @@ -68,6 +68,13 @@ func main() { buildDirectoryPath, commandCreator, configuration.SetTmpdirEnvironmentVariable) + for _, mountinfo := range configuration.InputRootMounts { + r = runner.NewMountingRunner( + r, + buildDirectory, + mountinfo, + ) + } // Let bb_runner replace temporary directories with symbolic // links pointing to the temporary directory set up by diff --git a/cmd/bb_scheduler/main.go b/cmd/bb_scheduler/main.go index 40a06deb..b7fd6f9b 100644 --- a/cmd/bb_scheduler/main.go +++ b/cmd/bb_scheduler/main.go @@ -108,7 +108,7 @@ func main() { } killOperationsAuthorizer, err := authorizerFactory.NewAuthorizerFromConfiguration(configuration.KillOperationsAuthorizer) if err != nil { - return util.StatusWrap(err, "Failed to create kill operaitons authorizer") + return util.StatusWrap(err, "Failed to create kill operations authorizer") } platformQueueWithNoWorkersTimeout := configuration.PlatformQueueWithNoWorkersTimeout diff --git a/pkg/filesystem/lazy_directory.go b/pkg/filesystem/lazy_directory.go index 78abceb6..96de17e3 100644 --- a/pkg/filesystem/lazy_directory.go +++ b/pkg/filesystem/lazy_directory.go @@ -245,3 +245,21 @@ func (d *lazyDirectory) Apply(arg interface{}) error { defer underlying.Close() return underlying.Apply(arg) } + +func (d *lazyDirectory) Mount(mountpoint path.Component, source string, fstype string) error { + underlying, err := d.openUnderlying() + if err != nil { + return err + } + defer underlying.Close() + return underlying.Mount(mountpoint, source, fstype) +} + +func (d *lazyDirectory) Unmount(mountpoint path.Component) error { + underlying, err := d.openUnderlying() + if err != nil { + return err + } + defer underlying.Close() + return underlying.Unmount(mountpoint) +} diff --git a/pkg/proto/configuration/bb_runner/bb_runner.pb.go b/pkg/proto/configuration/bb_runner/bb_runner.pb.go index 4a1356d7..2b7e15da 100644 --- a/pkg/proto/configuration/bb_runner/bb_runner.pb.go +++ b/pkg/proto/configuration/bb_runner/bb_runner.pb.go @@ -41,6 +41,7 @@ type ApplicationConfiguration struct { SymlinkTemporaryDirectories []string `protobuf:"bytes,12,rep,name=symlink_temporary_directories,json=symlinkTemporaryDirectories,proto3" json:"symlink_temporary_directories,omitempty"` RunCommandCleaner []string `protobuf:"bytes,13,rep,name=run_command_cleaner,json=runCommandCleaner,proto3" json:"run_command_cleaner,omitempty"` AppleXcodeDeveloperDirectories map[string]string `protobuf:"bytes,14,rep,name=apple_xcode_developer_directories,json=appleXcodeDeveloperDirectories,proto3" json:"apple_xcode_developer_directories,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + InputRootMounts []*MountInfo `protobuf:"bytes,15,rep,name=input_root_mounts,json=inputRootMounts,proto3" json:"input_root_mounts,omitempty"` } func (x *ApplicationConfiguration) Reset() { @@ -166,6 +167,76 @@ func (x *ApplicationConfiguration) GetAppleXcodeDeveloperDirectories() map[strin return nil } +func (x *ApplicationConfiguration) GetInputRootMounts() []*MountInfo { + if x != nil { + return x.InputRootMounts + } + return nil +} + +type MountInfo struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Mountpoint string `protobuf:"bytes,1,opt,name=mountpoint,proto3" json:"mountpoint,omitempty"` + Source string `protobuf:"bytes,2,opt,name=source,proto3" json:"source,omitempty"` + FilesystemType string `protobuf:"bytes,3,opt,name=filesystem_type,json=filesystemType,proto3" json:"filesystem_type,omitempty"` +} + +func (x *MountInfo) Reset() { + *x = MountInfo{} + if protoimpl.UnsafeEnabled { + mi := &file_pkg_proto_configuration_bb_runner_bb_runner_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *MountInfo) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*MountInfo) ProtoMessage() {} + +func (x *MountInfo) ProtoReflect() protoreflect.Message { + mi := &file_pkg_proto_configuration_bb_runner_bb_runner_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use MountInfo.ProtoReflect.Descriptor instead. +func (*MountInfo) Descriptor() ([]byte, []int) { + return file_pkg_proto_configuration_bb_runner_bb_runner_proto_rawDescGZIP(), []int{1} +} + +func (x *MountInfo) GetMountpoint() string { + if x != nil { + return x.Mountpoint + } + return "" +} + +func (x *MountInfo) GetSource() string { + if x != nil { + return x.Source + } + return "" +} + +func (x *MountInfo) GetFilesystemType() string { + if x != nil { + return x.FilesystemType + } + return "" +} + var File_pkg_proto_configuration_bb_runner_bb_runner_proto protoreflect.FileDescriptor var file_pkg_proto_configuration_bb_runner_bb_runner_proto_rawDesc = []byte{ @@ -183,7 +254,7 @@ var file_pkg_proto_configuration_bb_runner_bb_runner_proto_rawDesc = []byte{ 0x6f, 0x62, 0x61, 0x6c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x27, 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x67, 0x72, 0x70, 0x63, 0x2f, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x22, 0xf3, 0x08, 0x0a, 0x18, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, + 0x6f, 0x74, 0x6f, 0x22, 0xce, 0x09, 0x0a, 0x18, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x30, 0x0a, 0x14, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, @@ -249,17 +320,30 @@ var file_pkg_proto_configuration_bb_runner_bb_runner_proto_rawDesc = []byte{ 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x69, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x1e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x58, 0x63, 0x6f, 0x64, 0x65, 0x44, 0x65, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x72, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x69, 0x65, 0x73, - 0x1a, 0x51, 0x0a, 0x23, 0x41, 0x70, 0x70, 0x6c, 0x65, 0x58, 0x63, 0x6f, 0x64, 0x65, 0x44, 0x65, - 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x72, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 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, 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, 0x4a, 0x04, 0x08, 0x09, 0x10, 0x0a, 0x42, 0x4c, 0x5a, 0x4a, 0x67, 0x69, 0x74, - 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x62, 0x61, 0x72, - 0x6e, 0x2f, 0x62, 0x62, 0x2d, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x2d, 0x65, 0x78, 0x65, 0x63, - 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, - 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x62, 0x62, - 0x5f, 0x72, 0x75, 0x6e, 0x6e, 0x65, 0x72, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x12, 0x59, 0x0a, 0x11, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x5f, 0x6d, + 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x18, 0x0f, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x62, 0x75, + 0x69, 0x6c, 0x64, 0x62, 0x61, 0x72, 0x6e, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x62, 0x62, 0x5f, 0x72, 0x75, 0x6e, 0x6e, 0x65, 0x72, 0x2e, + 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x52, 0x0f, 0x69, 0x6e, 0x70, 0x75, + 0x74, 0x52, 0x6f, 0x6f, 0x74, 0x4d, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x1a, 0x51, 0x0a, 0x23, 0x41, + 0x70, 0x70, 0x6c, 0x65, 0x58, 0x63, 0x6f, 0x64, 0x65, 0x44, 0x65, 0x76, 0x65, 0x6c, 0x6f, 0x70, + 0x65, 0x72, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 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, 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, 0x4a, 0x04, + 0x08, 0x09, 0x10, 0x0a, 0x22, 0x6d, 0x0a, 0x0a, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x69, 0x6e, + 0x66, 0x6f, 0x12, 0x1e, 0x0a, 0x0a, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x70, 0x6f, 0x69, + 0x6e, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x66, 0x69, + 0x6c, 0x65, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0e, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x54, + 0x79, 0x70, 0x65, 0x42, 0x4c, 0x5a, 0x4a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, + 0x6d, 0x2f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x62, 0x61, 0x72, 0x6e, 0x2f, 0x62, 0x62, 0x2d, 0x72, + 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x2d, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x2f, + 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, + 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x62, 0x62, 0x5f, 0x72, 0x75, 0x6e, 0x6e, 0x65, + 0x72, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -274,26 +358,28 @@ func file_pkg_proto_configuration_bb_runner_bb_runner_proto_rawDescGZIP() []byte return file_pkg_proto_configuration_bb_runner_bb_runner_proto_rawDescData } -var file_pkg_proto_configuration_bb_runner_bb_runner_proto_msgTypes = make([]protoimpl.MessageInfo, 2) +var file_pkg_proto_configuration_bb_runner_bb_runner_proto_msgTypes = make([]protoimpl.MessageInfo, 3) var file_pkg_proto_configuration_bb_runner_bb_runner_proto_goTypes = []interface{}{ - (*ApplicationConfiguration)(nil), // 0: buildbarn.configuration.bb_runner.ApplicationConfiguration - nil, // 1: buildbarn.configuration.bb_runner.ApplicationConfiguration.AppleXcodeDeveloperDirectoriesEntry - (*grpc.ServerConfiguration)(nil), // 2: buildbarn.configuration.grpc.ServerConfiguration - (*global.Configuration)(nil), // 3: buildbarn.configuration.global.Configuration - (*grpc.ClientConfiguration)(nil), // 4: buildbarn.configuration.grpc.ClientConfiguration - (*credentials.UNIXCredentialsConfiguration)(nil), // 5: buildbarn.configuration.credentials.UNIXCredentialsConfiguration + (*ApplicationConfiguration)(nil), // 0: buildbarn.configuration.bb_runner.ApplicationConfiguration + (*MountInfo)(nil), // 1: buildbarn.configuration.bb_runner.mount_info + nil, // 2: buildbarn.configuration.bb_runner.ApplicationConfiguration.AppleXcodeDeveloperDirectoriesEntry + (*grpc.ServerConfiguration)(nil), // 3: buildbarn.configuration.grpc.ServerConfiguration + (*global.Configuration)(nil), // 4: buildbarn.configuration.global.Configuration + (*grpc.ClientConfiguration)(nil), // 5: buildbarn.configuration.grpc.ClientConfiguration + (*credentials.UNIXCredentialsConfiguration)(nil), // 6: buildbarn.configuration.credentials.UNIXCredentialsConfiguration } var file_pkg_proto_configuration_bb_runner_bb_runner_proto_depIdxs = []int32{ - 2, // 0: buildbarn.configuration.bb_runner.ApplicationConfiguration.grpc_servers:type_name -> buildbarn.configuration.grpc.ServerConfiguration - 3, // 1: buildbarn.configuration.bb_runner.ApplicationConfiguration.global:type_name -> buildbarn.configuration.global.Configuration - 4, // 2: buildbarn.configuration.bb_runner.ApplicationConfiguration.temporary_directory_installer:type_name -> buildbarn.configuration.grpc.ClientConfiguration - 5, // 3: buildbarn.configuration.bb_runner.ApplicationConfiguration.run_commands_as:type_name -> buildbarn.configuration.credentials.UNIXCredentialsConfiguration - 1, // 4: buildbarn.configuration.bb_runner.ApplicationConfiguration.apple_xcode_developer_directories:type_name -> buildbarn.configuration.bb_runner.ApplicationConfiguration.AppleXcodeDeveloperDirectoriesEntry - 5, // [5:5] is the sub-list for method output_type - 5, // [5:5] is the sub-list for method input_type - 5, // [5:5] is the sub-list for extension type_name - 5, // [5:5] is the sub-list for extension extendee - 0, // [0:5] is the sub-list for field type_name + 3, // 0: buildbarn.configuration.bb_runner.ApplicationConfiguration.grpc_servers:type_name -> buildbarn.configuration.grpc.ServerConfiguration + 4, // 1: buildbarn.configuration.bb_runner.ApplicationConfiguration.global:type_name -> buildbarn.configuration.global.Configuration + 5, // 2: buildbarn.configuration.bb_runner.ApplicationConfiguration.temporary_directory_installer:type_name -> buildbarn.configuration.grpc.ClientConfiguration + 6, // 3: buildbarn.configuration.bb_runner.ApplicationConfiguration.run_commands_as:type_name -> buildbarn.configuration.credentials.UNIXCredentialsConfiguration + 2, // 4: buildbarn.configuration.bb_runner.ApplicationConfiguration.apple_xcode_developer_directories:type_name -> buildbarn.configuration.bb_runner.ApplicationConfiguration.AppleXcodeDeveloperDirectoriesEntry + 1, // 5: buildbarn.configuration.bb_runner.ApplicationConfiguration.input_root_mounts:type_name -> buildbarn.configuration.bb_runner.mount_info + 6, // [6:6] is the sub-list for method output_type + 6, // [6:6] is the sub-list for method input_type + 6, // [6:6] is the sub-list for extension type_name + 6, // [6:6] is the sub-list for extension extendee + 0, // [0:6] is the sub-list for field type_name } func init() { file_pkg_proto_configuration_bb_runner_bb_runner_proto_init() } @@ -314,6 +400,18 @@ func file_pkg_proto_configuration_bb_runner_bb_runner_proto_init() { return nil } } + file_pkg_proto_configuration_bb_runner_bb_runner_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*MountInfo); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } } type x struct{} out := protoimpl.TypeBuilder{ @@ -321,7 +419,7 @@ func file_pkg_proto_configuration_bb_runner_bb_runner_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_pkg_proto_configuration_bb_runner_bb_runner_proto_rawDesc, NumEnums: 0, - NumMessages: 2, + NumMessages: 3, NumExtensions: 0, NumServices: 0, }, diff --git a/pkg/proto/configuration/bb_runner/bb_runner.proto b/pkg/proto/configuration/bb_runner/bb_runner.proto index 94c52514..b1446a80 100644 --- a/pkg/proto/configuration/bb_runner/bb_runner.proto +++ b/pkg/proto/configuration/bb_runner/bb_runner.proto @@ -131,4 +131,23 @@ message ApplicationConfiguration { // https://github.com/bazelbuild/bazel/blob/master/src/main/java/com/google/devtools/build/lib/exec/local/XcodeLocalEnvProvider.java // https://www.smileykeith.com/2021/03/08/locking-xcode-in-bazel/ map apple_xcode_developer_directories = 14; + + // Mount special filesystems in the input root. + // This is useful when running with `chroot`. + // some tools require access to special filesystems + // that are created when the operating system boots. + // An input root with a full userland implementation may need these. + // Typical choices are: + // {"proc", "/proc", "proc"} + // {"sys", "/sys", "sysfs"} + repeated mount_info input_root_mounts = 15; +} + +message mount_info { + // Mount a filesystem in the input root, a relative path. + string mountpoint = 1; + // Source filesystem from the runner's operating system, absolute path. + string source = 2; + // Type of filesystem, see `mount`'s man page. + string filesystem_type = 3; } diff --git a/pkg/runner/BUILD.bazel b/pkg/runner/BUILD.bazel index de0c7fe9..db8d0e7d 100644 --- a/pkg/runner/BUILD.bazel +++ b/pkg/runner/BUILD.bazel @@ -11,6 +11,7 @@ go_library( "local_runner_rss_kibibytes.go", "local_runner_unix.go", "local_runner_windows.go", + "mounting_runner.go", "path_existence_checking_runner.go", "temporary_directory_installing_runner.go", "temporary_directory_symlinking_runner.go", @@ -19,6 +20,7 @@ go_library( visibility = ["//visibility:public"], deps = [ "//pkg/cleaner", + "//pkg/proto/configuration/bb_runner", "//pkg/proto/runner", "//pkg/proto/tmp_installer", "@com_github_buildbarn_bb_storage//pkg/filesystem", diff --git a/pkg/runner/mounting_runner.go b/pkg/runner/mounting_runner.go new file mode 100644 index 00000000..6dd0d78e --- /dev/null +++ b/pkg/runner/mounting_runner.go @@ -0,0 +1,101 @@ +package runner + +import ( + "context" + "os" + "strings" + + "github.com/buildbarn/bb-remote-execution/pkg/proto/configuration/bb_runner" + runner_pb "github.com/buildbarn/bb-remote-execution/pkg/proto/runner" + "github.com/buildbarn/bb-storage/pkg/filesystem" + "github.com/buildbarn/bb-storage/pkg/filesystem/path" + "github.com/buildbarn/bb-storage/pkg/util" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + "google.golang.org/protobuf/types/known/emptypb" +) + +type mountingRunner struct { + base runner_pb.RunnerServer + buildDirectory filesystem.Directory + mount *bb_runner.MountInfo +} + +// NewMountingRunner is a decorator for Runner +// that mounts `mount` before running a build action. +// +// This decorator can be used for chroot runners +// that must mount special filesystems into the input root. +func NewMountingRunner(base runner_pb.RunnerServer, buildDirectory filesystem.Directory, mount *bb_runner.MountInfo) runner_pb.RunnerServer { + return &mountingRunner{ + buildDirectory: buildDirectory, + mount: mount, + base: base, + } +} + +func open(buildDirectory filesystem.Directory, inputRootDirectory string) (filesystem.DirectoryCloser, error) { + // TODO(nils): How to best open the input root `Directory` + // It must be a `localDirectory`, but outer directories can be `lazyDirectory`. + // NB: We iterate over Directory, as the initial buildDirectory is not a DirectoryCloser. + iterator := buildDirectory + var dir filesystem.DirectoryCloser + + parts := strings.Split(inputRootDirectory, "/") + if len(parts) == 0 { + return nil, status.Error(codes.FailedPrecondition, "Unexpected empty inputRootDirectory") + } + for i, segment := range parts { + var err error + component := path.MustNewComponent(segment) + dir, err = iterator.EnterDirectory(component) + if i < len(parts) - 1 { + defer dir.Close() + } + + iterator = dir.(filesystem.Directory) + if err != nil { + return nil, status.Errorf(codes.FailedPrecondition, "Could not enter directory component '%s' in '%#v'", segment, buildDirectory) + } + } + return dir, nil +} + +func (r *mountingRunner) Run(ctx context.Context, request *runner_pb.RunRequest) (response *runner_pb.RunResponse, err error) { + root, err := open(r.buildDirectory, request.InputRootDirectory) + if err != nil { + return nil, status.Errorf(codes.FailedPrecondition, "Could not enter input root directory '%s'", request.InputRootDirectory) + } + defer root.Close() + + mountpoint := path.MustNewComponent(r.mount.Mountpoint) + err = root.Mkdir(mountpoint, 0o555) + if err != nil { + if ! os.IsExist(err) { + return nil, util.StatusWrapf(err, "Failed to create mount point: '%#v' in the input root", r.mount.Mountpoint) + } + } + + if err := root.Mount(mountpoint, r.mount.Source, r.mount.FilesystemType); err != nil { + return nil, util.StatusWrapf(err, "Failed to mount '%#v' in the input root", r.mount) + } + defer func() { + if err2 := root.Unmount(mountpoint); err2 != nil { + err = util.StatusFromMultiple([]error{ + err, + util.StatusWrap(err2, "Failed to unmount '%#v' in the input root"), + }) + } + }() + + response, err = r.base.Run(ctx, request) + if err != nil { + return nil, err + } + + return response, nil +} + +func (r *mountingRunner) CheckReadiness(ctx context.Context, request *emptypb.Empty) (*emptypb.Empty, error) { + return r.base.CheckReadiness(ctx, request) +}