Skip to content

Commit

Permalink
[RB] Support for macs (#7088)
Browse files Browse the repository at this point in the history
  • Loading branch information
maggie-lou authored Aug 5, 2024
1 parent e52476e commit 2ae1aa4
Show file tree
Hide file tree
Showing 15 changed files with 143 additions and 78 deletions.
4 changes: 2 additions & 2 deletions cli/remotebazel/remotebazel.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,8 @@ const (
var (
RemoteFlagset = flag.NewFlagSet("remote", flag.ContinueOnError)

execOs = RemoteFlagset.String("os", "linux", "If set, requests execution on a specific OS.")
execArch = RemoteFlagset.String("arch", "amd64", "If set, requests execution on a specific CPU architecture.")
execOs = RemoteFlagset.String("os", "", "If set, requests execution on a specific OS.")
execArch = RemoteFlagset.String("arch", "", "If set, requests execution on a specific CPU architecture.")
containerImage = RemoteFlagset.String("container_image", "", "If set, requests execution on a specific runner image. Otherwise uses the default hosted runner version. A `docker://` prefix is required.")
envInput = bbflag.New(RemoteFlagset, "env", []string{}, "Environment variables to set in the runner environment. Key-value pairs can either be separated by '=' (Ex. --env=k1=val1), or if only a key is specified, the value will be taken from the invocation environment (Ex. --env=k2). To apply multiple env vars, pass the env flag multiple times (Ex. --env=k1=v1 --env=k2). If the same key is given twice, the latest will apply.")
remoteRunner = RemoteFlagset.String("remote_runner", defaultRemoteExecutionURL, "The Buildbuddy grpc target the remote runner should run on.")
Expand Down
13 changes: 7 additions & 6 deletions enterprise/config/buildbuddy.workflow.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,13 @@ app:
default_redis_target: "localhost:6379"
enable_target_tracking: true
user_owned_keys_enabled: true
# NOTE: The workflow runner cannot communicate with the local app server via localhost
# because they have different network stacks. Instead you must find and hardcode
# the IP address of the machine the app server is running on
cache_api_url: "grpc://TODO:1985"
events_api_url: "grpc://TODO:1985"
remote_execution_api_url: "grpc://TODO:1985"
# TODO: Containerized workflow runners cannot communicate with the local app server via localhost
# because they have different network stacks. If using (i.e. on Linux), replace
# `localhost` in the following URLs with the IP address of the machine the
# app server is running on
cache_api_url: "grpc://localhost:1985"
events_api_url: "grpc://localhost:1985"
remote_execution_api_url: "grpc://localhost:1985"
database:
data_source: "sqlite3:///tmp/${USER}-buildbuddy-enterprise.db"
storage:
Expand Down
2 changes: 0 additions & 2 deletions enterprise/server/cmd/ci_runner/bundle/bundle.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,5 @@ import (
_ "embed"
)

const RunnerName = "buildbuddy_ci_runner"

//go:embed buildbuddy_ci_runner
var CiRunnerBytes []byte
2 changes: 1 addition & 1 deletion enterprise/server/hostedrunner/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ go_library(
srcs = ["hostedrunner.go"],
importpath = "github.com/buildbuddy-io/buildbuddy/enterprise/server/hostedrunner",
deps = [
"//enterprise/server/cmd/ci_runner/bundle",
"//enterprise/server/remote_execution/operation",
"//enterprise/server/remote_execution/platform",
"//enterprise/server/util/ci_runner_util",
"//enterprise/server/workflow/config",
"//proto:remote_execution_go_proto",
"//proto:resource_go_proto",
Expand Down
47 changes: 24 additions & 23 deletions enterprise/server/hostedrunner/hostedrunner.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@ import (
"context"
"encoding/base64"
"io"
"path/filepath"
"sort"
"strings"
"time"

"github.com/buildbuddy-io/buildbuddy/enterprise/server/remote_execution/operation"
"github.com/buildbuddy-io/buildbuddy/enterprise/server/remote_execution/platform"
"github.com/buildbuddy-io/buildbuddy/enterprise/server/util/ci_runner_util"
"github.com/buildbuddy-io/buildbuddy/enterprise/server/workflow/config"
"github.com/buildbuddy-io/buildbuddy/server/endpoint_urls/build_buddy_url"
"github.com/buildbuddy-io/buildbuddy/server/endpoint_urls/cache_api_url"
Expand All @@ -32,7 +32,6 @@ import (
"google.golang.org/protobuf/types/known/durationpb"
"gopkg.in/yaml.v2"

ci_runner_bundle "github.com/buildbuddy-io/buildbuddy/enterprise/server/cmd/ci_runner/bundle"
repb "github.com/buildbuddy-io/buildbuddy/proto/remote_execution"
rspb "github.com/buildbuddy-io/buildbuddy/proto/resource"
rnpb "github.com/buildbuddy-io/buildbuddy/proto/runner"
Expand Down Expand Up @@ -82,20 +81,8 @@ func (r *runnerService) createAction(ctx context.Context, req *rnpb.RunRequest,
if cache == nil {
return nil, status.UnavailableError("No cache configured.")
}
runnerBinDigest, err := cachetools.UploadBlobToCAS(ctx, r.env.GetByteStreamClient(), req.GetInstanceName(), repb.DigestFunction_BLAKE3, ci_runner_bundle.CiRunnerBytes)
if err != nil {
return nil, status.WrapError(err, "upload runner bin")
}
// Save this to use when constructing the command to run below.
runnerName := filepath.Base(ci_runner_bundle.RunnerName)
dir := &repb.Directory{
Files: []*repb.FileNode{{
Name: runnerName,
Digest: runnerBinDigest,
IsExecutable: true,
}},
}
inputRootDigest, err := cachetools.UploadProtoToCAS(ctx, cache, req.GetInstanceName(), repb.DigestFunction_BLAKE3, dir)

inputRootDigest, err := ci_runner_util.UploadInputRoot(ctx, r.env.GetByteStreamClient(), r.env.GetCache(), req.GetInstanceName(), req.GetOs(), req.GetArch())
if err != nil {
return nil, status.WrapError(err, "upload input root")
}
Expand Down Expand Up @@ -136,7 +123,7 @@ func (r *runnerService) createAction(ctx context.Context, req *rnpb.RunRequest,
}

args := []string{
"./" + runnerName,
"./" + ci_runner_util.ExecutableName,
"--bes_backend=" + events_api_url.String(),
"--cache_backend=" + cache_api_url.String(),
"--rbe_backend=" + remote_exec_api_url.String(),
Expand All @@ -149,6 +136,7 @@ func (r *runnerService) createAction(ctx context.Context, req *rnpb.RunRequest,
"--commit_sha=" + req.GetRepoState().GetCommitSha(),
"--target_branch=" + req.GetRepoState().GetBranch(),
"--serialized_action=" + serializedAction,
"--timeout=" + ci_runner_util.CIRunnerDefaultTimeout.String(),
}
if !req.GetRunRemotely() {
args = append(args, "--record_run_metadata")
Expand All @@ -166,11 +154,6 @@ func (r *runnerService) createAction(ctx context.Context, req *rnpb.RunRequest,
affinityKey = repoURL.String()
}

image := DefaultRunnerContainerImage
if req.GetContainerImage() != "" {
image = req.GetContainerImage()
}

// By default, use the non-root user as the operating user on the runner.
user := nonRootUser
for _, p := range req.ExecProperties {
Expand All @@ -180,11 +163,27 @@ func (r *runnerService) createAction(ctx context.Context, req *rnpb.RunRequest,
}
}

// Run from the scratch disk, since the workspace disk is hot-swapped
// between runs, which may not be very Bazel-friendly.
wd := "/home/buildbuddy/workspace"
if user == rootUser {
wd = "/root/workspace"
}

image := DefaultRunnerContainerImage
isolationType := "firecracker"

// Containers/VMs aren't supported on darwin - default to bare execution
// and use the action workspace as the working directory.
if req.GetOs() == "darwin" {
wd = ""
image = ""
isolationType = "none"
}
if req.GetContainerImage() != "" {
image = req.GetContainerImage()
}

// Hosted Bazel shares the same pool with workflows.
cmd := &repb.Command{
EnvironmentVariables: []*repb.Command_EnvironmentVariable{
Expand All @@ -200,7 +199,9 @@ func (r *runnerService) createAction(ctx context.Context, req *rnpb.RunRequest,
{Name: platform.HostedBazelAffinityKeyPropertyName, Value: affinityKey},
{Name: "container-image", Value: image},
{Name: "recycle-runner", Value: "true"},
{Name: "workload-isolation-type", Value: "firecracker"},
{Name: "runner-recycling-max-wait", Value: (*ci_runner_util.RecycledCIRunnerMaxWait).String()},
{Name: "preserve-workspace", Value: "true"},
{Name: "workload-isolation-type", Value: isolationType},
{Name: platform.EstimatedComputeUnitsPropertyName, Value: "2"},
{Name: platform.EstimatedFreeDiskPropertyName, Value: "20000000000"}, // 20GB
{Name: platform.DockerUserPropertyName, Value: user},
Expand Down
4 changes: 3 additions & 1 deletion enterprise/server/remote_execution/platform/platform.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,9 @@ const (
DarwinOperatingSystemName = "darwin"

CPUArchitecturePropertyName = "Arch"
defaultCPUArchitecture = "amd64"
AMD64ArchitectureName = "amd64"
ARM64ArchitectureName = "arm64"
defaultCPUArchitecture = AMD64ArchitectureName

DockerInitPropertyName = "dockerInit"
DockerUserPropertyName = "dockerUser"
Expand Down
1 change: 1 addition & 0 deletions enterprise/server/remote_execution/runner/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ go_library(
"//enterprise/server/remote_execution/vfs",
"//enterprise/server/remote_execution/workspace",
"//enterprise/server/tasksize",
"//enterprise/server/util/ci_runner_util",
"//enterprise/server/util/oci",
"//enterprise/server/util/vfs_server",
"//proto:remote_execution_go_proto",
Expand Down
6 changes: 2 additions & 4 deletions enterprise/server/remote_execution/runner/runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (
"github.com/buildbuddy-io/buildbuddy/enterprise/server/remote_execution/vfs"
"github.com/buildbuddy-io/buildbuddy/enterprise/server/remote_execution/workspace"
"github.com/buildbuddy-io/buildbuddy/enterprise/server/tasksize"
"github.com/buildbuddy-io/buildbuddy/enterprise/server/util/ci_runner_util"
"github.com/buildbuddy-io/buildbuddy/enterprise/server/util/oci"
"github.com/buildbuddy-io/buildbuddy/enterprise/server/util/vfs_server"
"github.com/buildbuddy-io/buildbuddy/server/environment"
Expand Down Expand Up @@ -308,10 +309,7 @@ func (r *taskRunner) DownloadInputs(ctx context.Context, ioStats *repb.IOStats)
if err != nil {
return err
}
// TODO(Maggie): Do not do this on Linux after we start uploading/downloading
// the binary from the cache
// TODO(Maggie): We'll need to do this even if WorkflowID == "" for remote bazel on macs
if r.PlatformProperties.WorkflowID != "" {
if platform.IsCICommand(r.task.GetCommand()) && !ci_runner_util.CanInitFromCache(r.PlatformProperties.OS, r.PlatformProperties.Arch) {
if err := r.Workspace.AddCIRunner(ctx); err != nil {
return err
}
Expand Down
1 change: 1 addition & 0 deletions enterprise/server/remote_execution/workspace/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ go_library(
"//enterprise/server/remote_execution/overlayfs",
"//enterprise/server/remote_execution/platform",
"//enterprise/server/remote_execution/vfs",
"//enterprise/server/util/ci_runner_util",
"//proto:remote_execution_go_proto",
"//server/environment",
"//server/interfaces",
Expand Down
3 changes: 2 additions & 1 deletion enterprise/server/remote_execution/workspace/workspace.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
"github.com/buildbuddy-io/buildbuddy/enterprise/server/remote_execution/overlayfs"
"github.com/buildbuddy-io/buildbuddy/enterprise/server/remote_execution/platform"
"github.com/buildbuddy-io/buildbuddy/enterprise/server/remote_execution/vfs"
"github.com/buildbuddy-io/buildbuddy/enterprise/server/util/ci_runner_util"
"github.com/buildbuddy-io/buildbuddy/server/environment"
"github.com/buildbuddy-io/buildbuddy/server/interfaces"
"github.com/buildbuddy-io/buildbuddy/server/remote_cache/cachetools"
Expand Down Expand Up @@ -201,7 +202,7 @@ func (ws *Workspace) DownloadInputs(ctx context.Context, tree *repb.Tree) (*dirt
// AddCIRunner adds the BuildBuddy CI runner to the workspace root if it doesn't
// already exist.
func (ws *Workspace) AddCIRunner(ctx context.Context) error {
destPath := path.Join(ws.Path(), ci_runner_bundle.RunnerName)
destPath := path.Join(ws.Path(), ci_runner_util.ExecutableName)
exists, err := disk.FileExists(ctx, destPath)
if err != nil {
return err
Expand Down
19 changes: 19 additions & 0 deletions enterprise/server/util/ci_runner_util/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
load("@io_bazel_rules_go//go:def.bzl", "go_library")

package(default_visibility = ["//enterprise:__subpackages__"])

go_library(
name = "ci_runner_util",
srcs = ["ci_runner_util.go"],
importpath = "github.com/buildbuddy-io/buildbuddy/enterprise/server/util/ci_runner_util",
deps = [
"//enterprise/server/cmd/ci_runner/bundle",
"//enterprise/server/remote_execution/platform",
"//proto:remote_execution_go_proto",
"//server/interfaces",
"//server/remote_cache/cachetools",
"//server/remote_cache/digest",
"//server/util/status",
"@org_golang_google_genproto_googleapis_bytestream//:bytestream",
],
)
70 changes: 70 additions & 0 deletions enterprise/server/util/ci_runner_util/ci_runner_util.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package ci_runner_util

import (
"context"
_ "embed"
"flag"
"path/filepath"
"time"

"github.com/buildbuddy-io/buildbuddy/enterprise/server/cmd/ci_runner/bundle"
"github.com/buildbuddy-io/buildbuddy/enterprise/server/remote_execution/platform"
"github.com/buildbuddy-io/buildbuddy/server/interfaces"
"github.com/buildbuddy-io/buildbuddy/server/remote_cache/cachetools"
"github.com/buildbuddy-io/buildbuddy/server/remote_cache/digest"
"github.com/buildbuddy-io/buildbuddy/server/util/status"

repb "github.com/buildbuddy-io/buildbuddy/proto/remote_execution"
bspb "google.golang.org/genproto/googleapis/bytestream"
)

const ExecutableName = "buildbuddy_ci_runner"

var (
RecycledCIRunnerMaxWait = flag.Duration("remote_execution.ci_runner_recycling_max_wait", 3*time.Second, "Max duration that a ci_runner task should wait for a warm runner before running on a potentially cold runner.")
CIRunnerDefaultTimeout = flag.Duration("remote_execution.ci_runner_default_timeout", 8*time.Hour, "Default timeout applied to all ci runners.")
)

// CanInitFromCache The apps are built for linux/amd64. If the ci_runner will run on linux/amd64
// as well, the apps can upload the ci_runner binary to the cache for the executors
// to pull down. This will ensure the executors are using the most up-to-date
// version of the binary, even for customers using self-hosted executors that aren't
// on the latest executor version.
//
// If this returns false, the executor is responsible for ensuring the
// buildbuddy_ci_runner binary exists at the workspace root when it sees
// a ci_runner task. This will guarantee the binary is built for the correct os/arch,
// but it will not update automatically when the apps are upgraded.
func CanInitFromCache(os, arch string) bool {
return (os == "" || os == platform.LinuxOperatingSystemName) && (arch == "" || arch == platform.AMD64ArchitectureName)
}

// UploadInputRoot If the ci_runner can be properly initialized from the cache,
// upload the binary and return the input root digest containing the executable.
// If it can't, return an empty input root digest. In this case, the executor
// is responsible for ensuring the binary exists at the workspace root.
func UploadInputRoot(ctx context.Context, bsClient bspb.ByteStreamClient, cache interfaces.Cache, instanceName string, os string, arch string) (*repb.Digest, error) {
if CanInitFromCache(os, arch) {
if bsClient == nil {
return nil, status.UnavailableError("no bytestream client configured")
}
if cache == nil {
return nil, status.UnavailableError("no cache configured")
}

runnerBinDigest, err := cachetools.UploadBlobToCAS(ctx, bsClient, instanceName, repb.DigestFunction_BLAKE3, bundle.CiRunnerBytes)
if err != nil {
return nil, status.WrapError(err, "upload runner bin")
}
runnerName := filepath.Base(ExecutableName)
dir := &repb.Directory{
Files: []*repb.FileNode{{
Name: runnerName,
Digest: runnerBinDigest,
IsExecutable: true,
}},
}
return cachetools.UploadProtoToCAS(ctx, cache, instanceName, repb.DigestFunction_BLAKE3, dir)
}
return digest.ComputeForMessage(&repb.Directory{}, repb.DigestFunction_BLAKE3)
}
3 changes: 1 addition & 2 deletions enterprise/server/workflow/service/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ go_library(
srcs = ["service.go"],
importpath = "github.com/buildbuddy-io/buildbuddy/enterprise/server/workflow/service",
deps = [
"//enterprise/server/cmd/ci_runner/bundle",
"//enterprise/server/remote_execution/operation",
"//enterprise/server/remote_execution/platform",
"//enterprise/server/util/ci_runner_util",
"//enterprise/server/webhooks/webhook_data",
"//enterprise/server/workflow/config",
"//proto:context_go_proto",
Expand All @@ -26,7 +26,6 @@ go_library(
"//server/interfaces",
"//server/metrics",
"//server/remote_cache/cachetools",
"//server/remote_cache/digest",
"//server/remote_execution/config",
"//server/tables",
"//server/util/alert",
Expand Down
Loading

0 comments on commit 2ae1aa4

Please sign in to comment.