Skip to content
Open
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
2 changes: 2 additions & 0 deletions sandboxexec/sandbox/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ go_library(
srcs = [
"oci.go",
"sandbox.go",
"storage.go",
],
visibility = ["//:__subpackages__"],
deps = ["@com_github_opencontainers_runtime_spec//specs-go:go_default_library"],
Expand All @@ -20,6 +21,7 @@ go_test(
srcs = [
"oci_test.go",
"sandbox_test.go",
"storage_test.go",
],
data = ["//runsc"],
tags = ["nogotsan"],
Expand Down
28 changes: 18 additions & 10 deletions sandboxexec/sandbox/oci.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,19 @@ import (
specs "github.com/opencontainers/runtime-spec/specs-go"
)

// NewBundle creates a temporary OCI bundle on the fly.
func NewBundle(sandboxID string, runscRuntimeDir string, enableNetworking bool, mounts []Mount) (string, error) {
// BundleConfig holds configuration for creating an OCI bundle.
type BundleConfig struct {
ID string
RuntimeDir string
EnableNetworking bool
Mounts []Mount
Annotations map[string]string
}

// NewBundle creates a temporary OCI bundle on the fly with the given configuration.
func NewBundle(cfg BundleConfig) (string, error) {
// Create a bundle directory for the sandbox.
bundleDir := filepath.Join(runscRuntimeDir, sandboxID)
bundleDir := filepath.Join(cfg.RuntimeDir, cfg.ID)
rootfsDir := filepath.Join(bundleDir, "rootfs")

if err := os.MkdirAll(rootfsDir, 0755); err != nil {
Expand All @@ -44,17 +53,16 @@ func NewBundle(sandboxID string, runscRuntimeDir string, enableNetworking bool,
if os.Geteuid() != 0 {
namespaces = append(namespaces, specs.LinuxNamespace{Type: specs.UserNamespace})
}
if enableNetworking {
if cfg.EnableNetworking {
namespaces = append(namespaces, specs.LinuxNamespace{Type: specs.NetworkNamespace})
}

spec := &specs.Spec{
Version: "1.0.0",
Version: "1.0.0",
Annotations: cfg.Annotations,
Root: &specs.Root{
Path: "rootfs",
// The root filesystem is read-only for now. We can add support for
// writable rootfs later if needed.
Readonly: true,
Path: "rootfs",
Readonly: false,
},
Process: &specs.Process{
Terminal: false,
Expand Down Expand Up @@ -103,7 +111,7 @@ func NewBundle(sandboxID string, runscRuntimeDir string, enableNetworking bool,

// Add custom mounts. Custom mounts overriding default host mounts create duplicate OCI
// entries. The later entry overrides the earlier one, as expected by OCI specs.
for _, m := range mounts {
for _, m := range cfg.Mounts {
switch m.Type {
case MountTypeBind:
opts := []string{"rbind"}
Expand Down
52 changes: 47 additions & 5 deletions sandboxexec/sandbox/oci_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,18 +31,20 @@ func TestNewBundle(t *testing.T) {
tempDir := t.TempDir()
sandboxID := "test-sandbox"

bundleDir, err := sandbox.NewBundle(sandboxID, tempDir, enableNetworking, nil)
bundleDir, err := sandbox.NewBundle(sandbox.BundleConfig{
ID: sandboxID,
RuntimeDir: tempDir,
EnableNetworking: enableNetworking,
})
if err != nil {
t.Fatalf("NewBundle(enableNet=%v) failed: %v", enableNetworking, err)
}
defer os.RemoveAll(bundleDir)

expectedBundleDir := filepath.Join(tempDir, sandboxID)
if bundleDir != expectedBundleDir {
t.Fatalf("NewBundle(%v, %v) = %q, want %q", sandboxID, tempDir, bundleDir, expectedBundleDir)
}

// Verify config.json was created and contains valid OCI spec.
configPath := filepath.Join(bundleDir, "config.json")
configFile, err := os.Open(configPath)
if err != nil {
Expand All @@ -61,6 +63,9 @@ func TestNewBundle(t *testing.T) {
if spec.Root == nil || spec.Root.Path != "rootfs" {
t.Errorf("spec.Root.Path is not 'rootfs', got: %+v", spec.Root)
}
if spec.Root.Readonly {
t.Errorf("spec.Root.Readonly is true, want false")
}
if spec.Linux == nil {
t.Fatalf("spec.Linux is nil")
}
Expand Down Expand Up @@ -114,7 +119,11 @@ func TestNewBundleNormalization(t *testing.T) {
},
}

bundleDir, err := sandbox.NewBundle(sandboxID, tempDir, false, mounts)
bundleDir, err := sandbox.NewBundle(sandbox.BundleConfig{
ID: sandboxID,
RuntimeDir: tempDir,
Mounts: mounts,
})
if err != nil {
t.Fatalf("NewBundle failed: %v", err)
}
Expand All @@ -132,7 +141,6 @@ func TestNewBundleNormalization(t *testing.T) {
t.Fatalf("failed to decode config.json: %v", err)
}

// Verify our custom mounts are present and normalized
var foundBind, foundTmpfs bool
for _, m := range spec.Mounts {
if m.Type == "bind" && m.Destination == "/mnt/foo/bar" {
Expand All @@ -153,3 +161,37 @@ func TestNewBundleNormalization(t *testing.T) {
t.Errorf("failed to find normalized tmpfs mount '/mnt'")
}
}

func TestNewBundleWithAnnotations(t *testing.T) {
tempDir := t.TempDir()
sandboxID := "test-sandbox-annotations"
annotations := map[string]string{
"dev.gvisor.tar.rootfs.upper": "/tmp/test.tar",
}

bundleDir, err := sandbox.NewBundle(sandbox.BundleConfig{
ID: sandboxID,
RuntimeDir: tempDir,
Annotations: annotations,
})
if err != nil {
t.Fatalf("NewBundle failed: %v", err)
}
defer os.RemoveAll(bundleDir)

configPath := filepath.Join(bundleDir, "config.json")
configFile, err := os.Open(configPath)
if err != nil {
t.Fatalf("failed to open config.json: %v", err)
}
defer configFile.Close()

var spec specs.Spec
if err := json.NewDecoder(configFile).Decode(&spec); err != nil {
t.Fatalf("failed to decode config.json: %v", err)
}

if val, ok := spec.Annotations["dev.gvisor.tar.rootfs.upper"]; !ok || val != "/tmp/test.tar" {
t.Errorf("expected annotation 'dev.gvisor.tar.rootfs.upper' with value '/tmp/test.tar', got spec.Annotations: %+v", spec.Annotations)
}
}
Loading
Loading