Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 46 additions & 1 deletion enterprise/server/remote_execution/platform/platform.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ var (
dockerSocket = flag.String("executor.docker_socket", "", "If set, run execution commands in docker using the provided socket.")
defaultXcodeVersion = flag.String("executor.default_xcode_version", "", "Sets the default Xcode version number to use if an action doesn't specify one. If not set, /Applications/Xcode.app/ is used.")
defaultIsolationType = flag.String("executor.default_isolation_type", "", "The default workload isolation type when no type is specified in an action. If not set, we use the first of the following that is set: docker, podman, firecracker, or none (bare).")
enableAppleContainer = flag.Bool("executor.enable_applecontainer", false, "Enables running execution commands via Apple's container tool.")
enableBareRunner = flag.Bool("executor.enable_bare_runner", false, "Enables running execution commands directly on the host without isolation.")
enablePodman = flag.Bool("executor.enable_podman", false, "Enables running execution commands inside podman containers.")
enableOCI = flag.Bool("executor.enable_oci", false, "Enables running execution commands using an OCI runtime directly.")
Expand Down Expand Up @@ -160,6 +161,7 @@ const (
FirecrackerContainerType ContainerType = "firecracker"
OCIContainerType ContainerType = "oci"
SandboxContainerType ContainerType = "sandbox"
AppleContainerType ContainerType = "applecontainer"
// If you add a container type, also add it to KnownContainerTypes

// The app will mint a signed client identity token to workflows.
Expand All @@ -168,7 +170,7 @@ const (

// KnownContainerTypes are all the types that are currently supported, or were
// previously supported.
var KnownContainerTypes []ContainerType = []ContainerType{BareContainerType, PodmanContainerType, DockerContainerType, FirecrackerContainerType, OCIContainerType, SandboxContainerType}
var KnownContainerTypes []ContainerType = []ContainerType{BareContainerType, PodmanContainerType, DockerContainerType, FirecrackerContainerType, OCIContainerType, SandboxContainerType, AppleContainerType}

// CoerceContainerType returns t if it's empty or in KnownContainerTypes.
// Otherwise it returns "Unknown".
Expand Down Expand Up @@ -321,6 +323,8 @@ type ContainerType string
type ExecutorProperties struct {
SupportedIsolationTypes []ContainerType
DefaultXcodeVersion string
AdvertisedOSFamily string
AdvertisedArch string
}

func (p *ExecutorProperties) SupportsIsolation(c ContainerType) bool {
Expand Down Expand Up @@ -531,6 +535,16 @@ func GetExecutorProperties() *ExecutorProperties {
DefaultXcodeVersion: *defaultXcodeVersion,
}

if *enableAppleContainer {
if runtime.GOOS != "darwin" || runtime.GOARCH != "arm64" {
log.Warningf("Apple container runtime enabled, but executor host is %s/%s; startup will fail during validation.", runtime.GOOS, runtime.GOARCH)
}
p.SupportedIsolationTypes = append(p.SupportedIsolationTypes, AppleContainerType)
p.AdvertisedOSFamily = LinuxOperatingSystemName
p.AdvertisedArch = ARM64ArchitectureName
return p
}

// NB: order matters! this list will be used in order to determine the which
// isolation method to use if none was set.

Expand Down Expand Up @@ -584,6 +598,37 @@ func GetExecutorProperties() *ExecutorProperties {
}

func ValidateIsolationTypes() error {
if *enableAppleContainer {
if runtime.GOOS != "darwin" || runtime.GOARCH != "arm64" {
return status.InvalidArgumentErrorf("executor.enable_applecontainer requires a darwin/arm64 host (got %s/%s)", runtime.GOOS, runtime.GOARCH)
}
var conflicts []string
if *enableBareRunner {
conflicts = append(conflicts, "executor.enable_bare_runner")
}
if *enablePodman {
conflicts = append(conflicts, "executor.enable_podman")
}
if *enableOCI {
conflicts = append(conflicts, "executor.enable_oci")
}
if *enableSandbox {
conflicts = append(conflicts, "executor.enable_sandbox")
}
if *EnableFirecracker {
conflicts = append(conflicts, "executor.enable_firecracker")
}
if *dockerSocket != "" {
conflicts = append(conflicts, "executor.docker_socket")
}
if len(conflicts) > 0 {
return status.InvalidArgumentErrorf("executor.enable_applecontainer cannot be combined with %s", strings.Join(conflicts, ", "))
}
if *defaultIsolationType != "" && ContainerType(*defaultIsolationType) != AppleContainerType {
return status.InvalidArgumentErrorf("the configured 'default_isolation_type' %q is invalid when executor.enable_applecontainer is enabled; it must be %q", *defaultIsolationType, AppleContainerType)
}
}

if *defaultIsolationType == "" {
return nil
}
Expand Down
37 changes: 37 additions & 0 deletions enterprise/server/remote_execution/platform/platform_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"encoding/base64"
"encoding/json"
"fmt"
"runtime"
"strings"
"testing"
"time"
Expand All @@ -29,6 +30,42 @@ var (
podmanAndFirecracker = &ExecutorProperties{SupportedIsolationTypes: []ContainerType{PodmanContainerType, FirecrackerContainerType}}
)

func TestGetExecutorProperties_AppleContainerAdvertisesLinux(t *testing.T) {
flags.Set(t, "executor.enable_applecontainer", true)

props := GetExecutorProperties()
require.Len(t, props.SupportedIsolationTypes, 1)
assert.Equal(t, AppleContainerType, props.SupportedIsolationTypes[0])
assert.Equal(t, LinuxOperatingSystemName, props.AdvertisedOSFamily)
assert.Equal(t, ARM64ArchitectureName, props.AdvertisedArch)
}

func TestValidateIsolationTypes_AppleContainerHostRequirement(t *testing.T) {
flags.Set(t, "executor.enable_applecontainer", true)

err := ValidateIsolationTypes()
if runtime.GOOS == "darwin" && runtime.GOARCH == "arm64" {
require.NoError(t, err)
return
}
require.Error(t, err)
assert.True(t, status.IsInvalidArgumentError(err))
assert.Contains(t, err.Error(), "darwin/arm64")
}

func TestValidateIsolationTypes_AppleContainerConflicts(t *testing.T) {
if runtime.GOOS != "darwin" || runtime.GOARCH != "arm64" {
t.Skip("requires darwin/arm64 host")
}
flags.Set(t, "executor.enable_applecontainer", true)
flags.Set(t, "executor.enable_bare_runner", true)

err := ValidateIsolationTypes()
require.Error(t, err)
assert.True(t, status.IsInvalidArgumentError(err))
assert.Contains(t, err.Error(), "cannot be combined")
}

func TestParse_ContainerImage_Success(t *testing.T) {
flags.Set(t, "executor.container_registry_region", "us-test1")
for _, testCase := range []struct {
Expand Down
12 changes: 10 additions & 2 deletions enterprise/server/scheduling/scheduler_client/scheduler_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,14 @@ func makeExecutionNode(pool, executorID, executorHostID string, xcodeLocator int
for _, t := range executorProps.SupportedIsolationTypes {
supportedTypes = append(supportedTypes, string(t))
}
osFamily := executorProps.AdvertisedOSFamily
if osFamily == "" {
osFamily = resources.GetOSFamily()
}
arch := executorProps.AdvertisedArch
if arch == "" {
arch = resources.GetArch()
}

return &scpb.ExecutionNode{
Host: hostname,
Expand All @@ -78,9 +86,9 @@ func makeExecutionNode(pool, executorID, executorHostID string, xcodeLocator int
AssignableMemoryBytes: resources.GetAllocatedRAMBytes(),
AssignableMilliCpu: resources.GetAllocatedCPUMillis(),
AssignableCustomResources: resources.GetAllocatedCustomResources(),
OsFamily: resources.GetOSFamily(),
OsFamily: osFamily,
OsDisplayName: resources.GetOSDisplayName(),
Arch: resources.GetArch(),
Arch: arch,
Pool: strings.ToLower(pool),
Version: version.Tag(),
ExecutorId: executorID,
Expand Down
Loading