e0e362d700
SDK Tests / changes (push) Successful in 2m29s
Real E2E Tests / changes (push) Successful in 2m29s
Deploy Docs Pages / build (push) Has been cancelled
Deploy Docs Pages / deploy (push) Has been cancelled
Real E2E Tests / JavaScript E2E (docker bridge) (push) Has been cancelled
Real E2E Tests / Python E2E (docker bridge) (push) Has been cancelled
Real E2E Tests / Java E2E (docker bridge) (push) Has been cancelled
Real E2E Tests / C# E2E (docker bridge) (push) Has been cancelled
Real E2E Tests / Go E2E (docker bridge) (push) Has been cancelled
Real E2E Tests / Real E2E CI (push) Has been cancelled
SDK Tests / SDK CI (push) Has been cancelled
SDK Tests / CLI Tests (push) Has been cancelled
SDK Tests / Python SDK Quality (code-interpreter) (push) Has been cancelled
SDK Tests / Python SDK Quality (sandbox) (push) Has been cancelled
SDK Tests / Python SDK Tests (code-interpreter) (push) Has been cancelled
SDK Tests / JavaScript SDK Quality And Tests (code-interpreter) (push) Has been cancelled
SDK Tests / JavaScript SDK Quality And Tests (sandbox) (push) Has been cancelled
SDK Tests / Python SDK Tests (sandbox) (push) Has been cancelled
SDK Tests / CLI Quality (push) Has been cancelled
SDK Tests / Kotlin SDK Quality And Tests (sandbox) (push) Has been cancelled
SDK Tests / Kotlin SDK Quality And Tests (code-interpreter) (push) Has been cancelled
SDK Tests / C# SDK Quality And Tests (code-interpreter) (push) Has been cancelled
SDK Tests / C# SDK Quality And Tests (sandbox) (push) Has been cancelled
SDK Tests / Go SDK Quality And Tests (push) Has been cancelled
340 lines
9.3 KiB
Go
340 lines
9.3 KiB
Go
// Copyright 2026 Alibaba Group Holding Ltd.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
//go:build linux && bwrap
|
|
|
|
package bwrap_test
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/alibaba/opensandbox/execd/pkg/runtime"
|
|
)
|
|
|
|
func TestFilesystem_ReadLower(t *testing.T) {
|
|
r := newRunner(t)
|
|
|
|
wsDir := "/tmp/bwrap-test-fs-lower"
|
|
require.NoError(t, os.MkdirAll(wsDir, 0o755))
|
|
defer os.RemoveAll(wsDir)
|
|
require.NoError(t, os.WriteFile(filepath.Join(wsDir, "readme.txt"), []byte("hello-lower"), 0o644))
|
|
|
|
opts := &runtime.IsolatedSessionOptions{
|
|
Profile: "balanced", WorkspacePath: wsDir, WorkspaceMode: "rw",
|
|
}
|
|
id, err := r.CreateIsolatedSession(opts)
|
|
require.NoError(t, err)
|
|
defer r.DeleteIsolatedSession(id)
|
|
|
|
mv, err := r.GetMergedView(id)
|
|
require.NoError(t, err)
|
|
|
|
// Read file from lower (workspace).
|
|
data, err := mv.ReadFile("readme.txt")
|
|
require.NoError(t, err)
|
|
assert.Equal(t, []byte("hello-lower"), data)
|
|
|
|
// Stat the file.
|
|
info, err := mv.Stat("readme.txt")
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "readme.txt", info.Name())
|
|
}
|
|
|
|
func TestFilesystem_WriteUpper(t *testing.T) {
|
|
r := newRunner(t)
|
|
|
|
wsDir := "/tmp/bwrap-test-fs-write"
|
|
require.NoError(t, os.MkdirAll(wsDir, 0o755))
|
|
defer os.RemoveAll(wsDir)
|
|
|
|
opts := &runtime.IsolatedSessionOptions{
|
|
Profile: "balanced", WorkspacePath: wsDir, WorkspaceMode: "rw",
|
|
}
|
|
id, err := r.CreateIsolatedSession(opts)
|
|
require.NoError(t, err)
|
|
defer r.DeleteIsolatedSession(id)
|
|
|
|
mv, err := r.GetMergedView(id)
|
|
require.NoError(t, err)
|
|
|
|
// Write a file via MergedView.
|
|
require.NoError(t, mv.WriteFile("session-file.txt", []byte("from-session"), 0o644))
|
|
|
|
// In rw mode, write goes directly to workspace (host view).
|
|
data, err := os.ReadFile(filepath.Join(wsDir, "session-file.txt"))
|
|
require.NoError(t, err)
|
|
assert.Equal(t, []byte("from-session"), data)
|
|
}
|
|
|
|
func TestFilesystem_WriteThenRead(t *testing.T) {
|
|
r := newRunner(t)
|
|
|
|
wsDir := "/tmp/bwrap-test-fs-rw"
|
|
require.NoError(t, os.MkdirAll(wsDir, 0o755))
|
|
defer os.RemoveAll(wsDir)
|
|
|
|
opts := &runtime.IsolatedSessionOptions{
|
|
Profile: "balanced", WorkspacePath: wsDir, WorkspaceMode: "rw",
|
|
}
|
|
id, err := r.CreateIsolatedSession(opts)
|
|
require.NoError(t, err)
|
|
defer r.DeleteIsolatedSession(id)
|
|
|
|
mv, err := r.GetMergedView(id)
|
|
require.NoError(t, err)
|
|
|
|
content := []byte("write-then-read")
|
|
require.NoError(t, mv.WriteFile("data.txt", content, 0o644))
|
|
|
|
data, err := mv.ReadFile("data.txt")
|
|
require.NoError(t, err)
|
|
assert.Equal(t, content, data)
|
|
}
|
|
|
|
func TestFilesystem_Delete(t *testing.T) {
|
|
r := newRunner(t)
|
|
|
|
wsDir := "/tmp/bwrap-test-fs-del"
|
|
require.NoError(t, os.MkdirAll(wsDir, 0o755))
|
|
defer os.RemoveAll(wsDir)
|
|
|
|
opts := &runtime.IsolatedSessionOptions{
|
|
Profile: "balanced", WorkspacePath: wsDir, WorkspaceMode: "rw",
|
|
}
|
|
id, err := r.CreateIsolatedSession(opts)
|
|
require.NoError(t, err)
|
|
defer r.DeleteIsolatedSession(id)
|
|
|
|
mv, err := r.GetMergedView(id)
|
|
require.NoError(t, err)
|
|
|
|
// Write then delete.
|
|
require.NoError(t, mv.WriteFile("del.txt", []byte("tmp"), 0o644))
|
|
require.NoError(t, mv.Remove("del.txt"))
|
|
|
|
_, err = mv.Stat("del.txt")
|
|
assert.True(t, os.IsNotExist(err))
|
|
}
|
|
|
|
func TestFilesystem_MkdirAndList(t *testing.T) {
|
|
r := newRunner(t)
|
|
|
|
wsDir := "/tmp/bwrap-test-fs-dir"
|
|
require.NoError(t, os.MkdirAll(wsDir, 0o755))
|
|
defer os.RemoveAll(wsDir)
|
|
|
|
opts := &runtime.IsolatedSessionOptions{
|
|
Profile: "balanced", WorkspacePath: wsDir, WorkspaceMode: "rw",
|
|
}
|
|
id, err := r.CreateIsolatedSession(opts)
|
|
require.NoError(t, err)
|
|
defer r.DeleteIsolatedSession(id)
|
|
|
|
mv, err := r.GetMergedView(id)
|
|
require.NoError(t, err)
|
|
|
|
require.NoError(t, mv.MkdirAll("a/b", 0o755))
|
|
require.NoError(t, mv.WriteFile("a/b/f.txt", []byte("nested"), 0o644))
|
|
|
|
entries, err := mv.ReadDir("a/b")
|
|
require.NoError(t, err)
|
|
names := make([]string, len(entries))
|
|
for i, e := range entries {
|
|
names[i] = e.Name()
|
|
}
|
|
assert.Contains(t, names, "f.txt")
|
|
}
|
|
|
|
func TestFilesystem_Rename(t *testing.T) {
|
|
r := newRunner(t)
|
|
|
|
wsDir := "/tmp/bwrap-test-fs-rename"
|
|
require.NoError(t, os.MkdirAll(wsDir, 0o755))
|
|
defer os.RemoveAll(wsDir)
|
|
|
|
opts := &runtime.IsolatedSessionOptions{
|
|
Profile: "balanced", WorkspacePath: wsDir, WorkspaceMode: "rw",
|
|
}
|
|
id, err := r.CreateIsolatedSession(opts)
|
|
require.NoError(t, err)
|
|
defer r.DeleteIsolatedSession(id)
|
|
|
|
mv, err := r.GetMergedView(id)
|
|
require.NoError(t, err)
|
|
|
|
require.NoError(t, mv.WriteFile("old.txt", []byte("renamed"), 0o644))
|
|
require.NoError(t, mv.Rename("old.txt", "new.txt"))
|
|
|
|
_, err = mv.Stat("old.txt")
|
|
assert.True(t, os.IsNotExist(err))
|
|
|
|
data, err := mv.ReadFile("new.txt")
|
|
require.NoError(t, err)
|
|
assert.Equal(t, []byte("renamed"), data)
|
|
}
|
|
|
|
func TestFilesystem_Chmod(t *testing.T) {
|
|
r := newRunner(t)
|
|
|
|
wsDir := "/tmp/bwrap-test-fs-chmod"
|
|
require.NoError(t, os.MkdirAll(wsDir, 0o755))
|
|
defer os.RemoveAll(wsDir)
|
|
|
|
opts := &runtime.IsolatedSessionOptions{
|
|
Profile: "balanced", WorkspacePath: wsDir, WorkspaceMode: "rw",
|
|
}
|
|
id, err := r.CreateIsolatedSession(opts)
|
|
require.NoError(t, err)
|
|
defer r.DeleteIsolatedSession(id)
|
|
|
|
mv, err := r.GetMergedView(id)
|
|
require.NoError(t, err)
|
|
|
|
require.NoError(t, mv.WriteFile("perm.txt", []byte("x"), 0o600))
|
|
require.NoError(t, mv.Chmod("perm.txt", 0o755))
|
|
|
|
info, err := mv.Stat("perm.txt")
|
|
require.NoError(t, err)
|
|
assert.Equal(t, os.FileMode(0o755), info.Mode().Perm())
|
|
}
|
|
|
|
func TestFilesystem_ReplaceContent(t *testing.T) {
|
|
r := newRunner(t)
|
|
|
|
wsDir := "/tmp/bwrap-test-fs-replace"
|
|
require.NoError(t, os.MkdirAll(wsDir, 0o755))
|
|
defer os.RemoveAll(wsDir)
|
|
|
|
opts := &runtime.IsolatedSessionOptions{
|
|
Profile: "balanced", WorkspacePath: wsDir, WorkspaceMode: "rw",
|
|
}
|
|
id, err := r.CreateIsolatedSession(opts)
|
|
require.NoError(t, err)
|
|
defer r.DeleteIsolatedSession(id)
|
|
|
|
mv, err := r.GetMergedView(id)
|
|
require.NoError(t, err)
|
|
|
|
require.NoError(t, mv.WriteFile("text.txt", []byte("abc def abc"), 0o644))
|
|
require.NoError(t, mv.ReplaceContent("text.txt", "abc", "xyz"))
|
|
|
|
data, err := mv.ReadFile("text.txt")
|
|
require.NoError(t, err)
|
|
assert.Equal(t, []byte("xyz def xyz"), data)
|
|
}
|
|
|
|
func TestFilesystem_Search(t *testing.T) {
|
|
r := newRunner(t)
|
|
|
|
wsDir := "/tmp/bwrap-test-fs-search"
|
|
require.NoError(t, os.MkdirAll(wsDir, 0o755))
|
|
defer os.RemoveAll(wsDir)
|
|
|
|
opts := &runtime.IsolatedSessionOptions{
|
|
Profile: "balanced", WorkspacePath: wsDir, WorkspaceMode: "rw",
|
|
}
|
|
id, err := r.CreateIsolatedSession(opts)
|
|
require.NoError(t, err)
|
|
defer r.DeleteIsolatedSession(id)
|
|
|
|
mv, err := r.GetMergedView(id)
|
|
require.NoError(t, err)
|
|
|
|
require.NoError(t, mv.WriteFile("a.txt", []byte("a"), 0o644))
|
|
require.NoError(t, mv.WriteFile("b.log", []byte("b"), 0o644))
|
|
require.NoError(t, os.WriteFile(filepath.Join(wsDir, "c.txt"), []byte("c"), 0o644))
|
|
|
|
results, err := mv.Search(".", "*.txt")
|
|
require.NoError(t, err)
|
|
assert.Len(t, results, 2)
|
|
assert.Contains(t, results, "a.txt")
|
|
assert.Contains(t, results, "c.txt")
|
|
}
|
|
|
|
func TestFilesystem_OverlayWriteNotVisibleOnHost(t *testing.T) {
|
|
r := newRunner(t)
|
|
|
|
wsDir := "/tmp/bwrap-test-fs-overlay"
|
|
require.NoError(t, os.MkdirAll(wsDir, 0o755))
|
|
defer os.RemoveAll(wsDir)
|
|
require.NoError(t, os.WriteFile(filepath.Join(wsDir, "lower.txt"), []byte("lower"), 0o644))
|
|
|
|
opts := &runtime.IsolatedSessionOptions{
|
|
Profile: "balanced", WorkspacePath: wsDir, WorkspaceMode: "overlay",
|
|
}
|
|
id, err := r.CreateIsolatedSession(opts)
|
|
require.NoError(t, err)
|
|
defer r.DeleteIsolatedSession(id)
|
|
|
|
mv, err := r.GetMergedView(id)
|
|
require.NoError(t, err)
|
|
|
|
// Write goes to upper, not lower.
|
|
require.NoError(t, mv.WriteFile("upper-only.txt", []byte("secret"), 0o644))
|
|
|
|
// NOT visible on host workspace.
|
|
_, err = os.ReadFile(filepath.Join(wsDir, "upper-only.txt"))
|
|
assert.True(t, os.IsNotExist(err))
|
|
|
|
// Visible via MergedView.
|
|
data, err := mv.ReadFile("upper-only.txt")
|
|
require.NoError(t, err)
|
|
assert.Equal(t, []byte("secret"), data)
|
|
|
|
// Lower file still visible.
|
|
data, err = mv.ReadFile("lower.txt")
|
|
require.NoError(t, err)
|
|
assert.Equal(t, []byte("lower"), data)
|
|
}
|
|
|
|
func TestFilesystem_ReadOnly(t *testing.T) {
|
|
r := newRunner(t)
|
|
|
|
wsDir := "/tmp/bwrap-test-fs-ro"
|
|
require.NoError(t, os.MkdirAll(wsDir, 0o755))
|
|
defer os.RemoveAll(wsDir)
|
|
require.NoError(t, os.WriteFile(filepath.Join(wsDir, "readme.txt"), []byte("ro-data"), 0o644))
|
|
|
|
opts := &runtime.IsolatedSessionOptions{
|
|
Profile: "balanced", WorkspacePath: wsDir, WorkspaceMode: "ro",
|
|
}
|
|
id, err := r.CreateIsolatedSession(opts)
|
|
require.NoError(t, err)
|
|
defer r.DeleteIsolatedSession(id)
|
|
|
|
mv, err := r.GetMergedView(id)
|
|
require.NoError(t, err)
|
|
|
|
// Read works.
|
|
data, err := mv.ReadFile("readme.txt")
|
|
require.NoError(t, err)
|
|
assert.Equal(t, []byte("ro-data"), data)
|
|
|
|
// Write denied.
|
|
assert.Error(t, mv.WriteFile("new.txt", []byte("x"), 0o644))
|
|
assert.Error(t, mv.Remove("readme.txt"))
|
|
assert.Error(t, mv.MkdirAll("d", 0o755))
|
|
}
|
|
|
|
func TestFilesystem_GetMergedView_NotFound(t *testing.T) {
|
|
r := newRunner(t)
|
|
_, err := r.GetMergedView("nonexistent")
|
|
assert.Error(t, err)
|
|
}
|