Skip to content

Commit

Permalink
Mount special filesystem in chroot runners
Browse files Browse the repository at this point in the history
This can be used to mount special filesystems like '/proc' and '/sys' in
the input root of actions if 'chroot' is enabled.
  • Loading branch information
stagnation committed Feb 1, 2024
1 parent a56ecc6 commit b1c0f7e
Show file tree
Hide file tree
Showing 6 changed files with 346 additions and 29 deletions.
7 changes: 7 additions & 0 deletions cmd/bb_runner/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
157 changes: 128 additions & 29 deletions pkg/proto/configuration/bb_runner/bb_runner.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

33 changes: 33 additions & 0 deletions pkg/proto/configuration/bb_runner/bb_runner.proto
Original file line number Diff line number Diff line change
Expand Up @@ -131,4 +131,37 @@ 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<string, string> apple_xcode_developer_directories = 14;

// Mount special filesystems in the input root. This is useful when
// running with `chroot_into_input_root`. 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.
//
// The mount point directories must exist in the input root.
//
// Typical choices are:
//
// inputRootMounts: [
// {
// mountpoint: 'proc',
// source: '/proc',
// filesystemType: 'proc',
// },
// {
// mountpoint: 'sys',
// source: '/sys',
// filesystemType: 'sysfs',
// },
// ],
repeated InputMountOptions input_root_mounts = 15;
}

message InputMountOptions {
// 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;
}
4 changes: 4 additions & 0 deletions pkg/runner/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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",
Expand Down Expand Up @@ -70,13 +72,15 @@ go_test(
"apple_xcode_resolving_runner_test.go",
"clean_runner_test.go",
"local_runner_test.go",
"mounting_runner_test.go",
"path_existence_checking_runner_test.go",
"temporary_directory_symlinking_runner_test.go",
],
deps = [
":runner",
"//internal/mock",
"//pkg/cleaner",
"//pkg/proto/configuration/bb_runner",
"//pkg/proto/resourceusage",
"//pkg/proto/runner",
"@com_github_buildbarn_bb_storage//pkg/filesystem",
Expand Down
75 changes: 75 additions & 0 deletions pkg/runner/mounting_runner.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package runner

import (
"context"
"path/filepath"

"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.InputMountOptions
}

// 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.InputMountOptions) runner_pb.RunnerServer {
return &mountingRunner{
buildDirectory: buildDirectory,
mount: mount,
base: base,
}
}

func (r *mountingRunner) Run(ctx context.Context, request *runner_pb.RunRequest) (response *runner_pb.RunResponse, err error) {
pathResolver := buildDirectoryPathResolver{
stack: util.NewNonEmptyStack(filesystem.NopDirectoryCloser(r.buildDirectory)),
}
scopeWalker := path.NewRelativeScopeWalker(&pathResolver)
defer pathResolver.closeAll()

fullpath := filepath.Join(request.InputRootDirectory, r.mount.Mountpoint)
if err := path.Resolve(fullpath, scopeWalker); err != nil {
return nil, util.StatusWrap(err, "Invalid mountpoint parent directory")
}

mountDir := pathResolver.stack.Peek()
defer mountDir.Close()
if pathResolver.TerminalName == nil {
return nil, status.Errorf(codes.InvalidArgument, "Could not resolve mountpoint basename: %#v", fullpath)
}

mountname := *pathResolver.TerminalName
if err := mountDir.Mount(mountname, r.mount.Source, r.mount.FilesystemType); err != nil {
return nil, util.StatusWrapf(err, "Failed to mount %#v in the input root", r.mount)
}

response, err = r.base.Run(ctx, request)
if err != nil {
return nil, err
}
if err2 := mountDir.Unmount(mountname); err2 != nil {
err = util.StatusFromMultiple([]error{
err,
util.StatusWrapf(err2, "Failed to unmount %#v in the input root", r.mount.Mountpoint),
})
}

return response, nil
}

func (r *mountingRunner) CheckReadiness(ctx context.Context, request *runner_pb.CheckReadinessRequest) (*emptypb.Empty, error) {
return r.base.CheckReadiness(ctx, request)
}
Loading

0 comments on commit b1c0f7e

Please sign in to comment.