/ tests / go / volume_e2e_test.go
volume_e2e_test.go
  1  // Copyright 2026 Alibaba Group Holding Ltd.
  2  //
  3  // Licensed under the Apache License, Version 2.0 (the "License");
  4  // you may not use this file except in compliance with the License.
  5  // You may obtain a copy of the License at
  6  //
  7  //     http://www.apache.org/licenses/LICENSE-2.0
  8  //
  9  // Unless required by applicable law or agreed to in writing, software
 10  // distributed under the License is distributed on an "AS IS" BASIS,
 11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 12  // See the License for the specific language governing permissions and
 13  // limitations under the License.
 14  
 15  package e2e
 16  
 17  import (
 18  	"context"
 19  	"os"
 20  	"strings"
 21  	"testing"
 22  	"time"
 23  
 24  	"github.com/alibaba/OpenSandbox/sdks/sandbox/go"
 25  	"github.com/stretchr/testify/require"
 26  )
 27  
 28  func getHostVolumeDir() string {
 29  	if v := os.Getenv("OPENSANDBOX_TEST_HOST_VOLUME_DIR"); v != "" {
 30  		return v
 31  	}
 32  	return "/tmp/opensandbox-e2e/host-volume-test"
 33  }
 34  
 35  func getPVCName() string {
 36  	if v := os.Getenv("OPENSANDBOX_TEST_PVC_NAME"); v != "" {
 37  		return v
 38  	}
 39  	return "opensandbox-e2e-pvc-test"
 40  }
 41  
 42  func TestVolume_HostMount(t *testing.T) {
 43  	config := getConnectionConfig(t)
 44  	ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
 45  	defer cancel()
 46  
 47  	hostDir := getHostVolumeDir()
 48  
 49  	sb, err := opensandbox.CreateSandbox(ctx, config, opensandbox.SandboxCreateOptions{
 50  		Image:        getSandboxImage(),
 51  		ReadyTimeout: 60 * time.Second,
 52  		Volumes: []opensandbox.Volume{
 53  			{
 54  				Name:      "test-host-vol",
 55  				Host:      &opensandbox.Host{Path: hostDir},
 56  				MountPath: "/mnt/host-data",
 57  			},
 58  		},
 59  	})
 60  	if err != nil {
 61  		t.Logf("CreateSandbox with host volume: %v (host volumes may not be allowed)", err)
 62  		t.Skip("Host volume mount not supported in this environment")
 63  	}
 64  	defer sb.Kill(context.Background())
 65  
 66  	exec, err := sb.RunCommand(ctx, `echo "host-mount-test" > /mnt/host-data/go-e2e.txt`, nil)
 67  	require.NoError(t, err)
 68  	if exec.ExitCode != nil {
 69  		require.Equal(t, 0, *exec.ExitCode, "write exit code")
 70  	}
 71  
 72  	exec, err = sb.RunCommand(ctx, "cat /mnt/host-data/go-e2e.txt", nil)
 73  	require.NoError(t, err)
 74  	require.Contains(t, exec.Text(), "host-mount-test")
 75  	t.Log("Host volume mount read/write passed")
 76  }
 77  
 78  func TestVolume_HostMountReadOnly(t *testing.T) {
 79  	config := getConnectionConfig(t)
 80  	ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
 81  	defer cancel()
 82  
 83  	hostDir := getHostVolumeDir()
 84  
 85  	sb, err := opensandbox.CreateSandbox(ctx, config, opensandbox.SandboxCreateOptions{
 86  		Image: getSandboxImage(),
 87  		Volumes: []opensandbox.Volume{
 88  			{
 89  				Name:      "test-host-ro",
 90  				Host:      &opensandbox.Host{Path: hostDir},
 91  				MountPath: "/mnt/host-ro",
 92  				ReadOnly:  true,
 93  			},
 94  		},
 95  	})
 96  	if err != nil {
 97  		t.Logf("CreateSandbox with ro host volume: %v", err)
 98  		t.Skip("Host volume mount not supported")
 99  	}
100  	defer sb.Kill(context.Background())
101  
102  	exec, err := sb.RunCommand(ctx, `echo "should-fail" > /mnt/host-ro/fail.txt 2>&1; echo "EXIT_CODE=$?"`, nil)
103  	require.NoError(t, err)
104  	output := exec.Text()
105  	require.NotContains(t, output, "EXIT_CODE=0", "write to read-only mount unexpectedly succeeded (exit code 0)")
106  	hasROError := strings.Contains(output, "Read-only") || strings.Contains(output, "read-only") ||
107  		strings.Contains(output, "Permission denied") || strings.Contains(output, "EXIT_CODE=1")
108  	require.True(t, hasROError, "expected read-only / permission denied / non-zero exit, got: %q", output)
109  	t.Log("Host volume read-only mount verified")
110  }
111  
112  func TestVolume_PVCMount(t *testing.T) {
113  	config := getConnectionConfig(t)
114  	ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
115  	defer cancel()
116  
117  	pvcName := getPVCName()
118  
119  	sb, err := opensandbox.CreateSandbox(ctx, config, opensandbox.SandboxCreateOptions{
120  		Image: getSandboxImage(),
121  		Volumes: []opensandbox.Volume{
122  			{
123  				Name:      "test-pvc-vol",
124  				PVC:       &opensandbox.PVC{ClaimName: pvcName},
125  				MountPath: "/mnt/pvc-data",
126  			},
127  		},
128  	})
129  	if err != nil {
130  		t.Logf("CreateSandbox with PVC: %v (PVC %s may not exist)", err, pvcName)
131  		t.Skip("PVC volume mount not available")
132  	}
133  	defer sb.Kill(context.Background())
134  
135  	exec, err := sb.RunCommand(ctx, `echo "pvc-test-data" > /mnt/pvc-data/go-e2e.txt`, nil)
136  	require.NoError(t, err)
137  	if exec.ExitCode != nil {
138  		require.Equal(t, 0, *exec.ExitCode, "write exit code")
139  	}
140  
141  	exec, err = sb.RunCommand(ctx, "cat /mnt/pvc-data/go-e2e.txt", nil)
142  	require.NoError(t, err)
143  	require.Contains(t, exec.Text(), "pvc-test-data")
144  	t.Log("PVC volume mount read/write passed")
145  }