11package instances
22
33import (
4+ "bytes"
45 "context"
56 "fmt"
67 "os"
@@ -11,6 +12,7 @@ import (
1112 "time"
1213
1314 "github.com/onkernel/hypeman/cmd/api/config"
15+ "github.com/onkernel/hypeman/lib/exec"
1416 "github.com/onkernel/hypeman/lib/images"
1517 "github.com/onkernel/hypeman/lib/network"
1618 "github.com/onkernel/hypeman/lib/paths"
@@ -200,7 +202,23 @@ func TestCreateAndDeleteInstance(t *testing.T) {
200202 require .NoError (t , err )
201203 t .Log ("System files ready" )
202204
203- // Create instance with real nginx image (stays running)
205+ // Create a volume to attach
206+ p := paths .New (tmpDir )
207+ volumeManager := volumes .NewManager (p )
208+ t .Log ("Creating volume..." )
209+ vol , err := volumeManager .CreateVolume (ctx , volumes.CreateVolumeRequest {
210+ Name : "test-data" ,
211+ SizeGb : 1 ,
212+ })
213+ require .NoError (t , err )
214+ require .NotNil (t , vol )
215+ t .Logf ("Volume created: %s" , vol .Id )
216+
217+ // Verify volume file exists and is not attached
218+ assert .FileExists (t , p .VolumeData (vol .Id ))
219+ assert .Nil (t , vol .AttachedTo , "Volume should not be attached yet" )
220+
221+ // Create instance with real nginx image and attached volume
204222 req := CreateInstanceRequest {
205223 Name : "test-nginx" ,
206224 Image : "docker.io/library/nginx:alpine" ,
@@ -212,6 +230,13 @@ func TestCreateAndDeleteInstance(t *testing.T) {
212230 Env : map [string ]string {
213231 "TEST_VAR" : "test_value" ,
214232 },
233+ Volumes : []VolumeAttachment {
234+ {
235+ VolumeID : vol .Id ,
236+ MountPath : "/mnt/data" ,
237+ Readonly : false ,
238+ },
239+ },
215240 }
216241
217242 t .Log ("Creating instance..." )
@@ -228,8 +253,19 @@ func TestCreateAndDeleteInstance(t *testing.T) {
228253 assert .False (t , inst .HasSnapshot )
229254 assert .NotEmpty (t , inst .KernelVersion )
230255
256+ // Verify volume is attached to instance
257+ assert .Len (t , inst .Volumes , 1 , "Instance should have 1 volume attached" )
258+ assert .Equal (t , vol .Id , inst .Volumes [0 ].VolumeID )
259+ assert .Equal (t , "/mnt/data" , inst .Volumes [0 ].MountPath )
260+
261+ // Verify volume shows as attached
262+ vol , err = volumeManager .GetVolume (ctx , vol .Id )
263+ require .NoError (t , err )
264+ require .NotNil (t , vol .AttachedTo , "Volume should be attached" )
265+ assert .Equal (t , inst .Id , * vol .AttachedTo )
266+ assert .Equal (t , "/mnt/data" , * vol .MountPath )
267+
231268 // Verify directories exist
232- p := paths .New (tmpDir )
233269 assert .DirExists (t , p .InstanceDir (inst .Id ))
234270 assert .FileExists (t , p .InstanceMetadata (inst .Id ))
235271 assert .FileExists (t , p .InstanceOverlay (inst .Id ))
@@ -270,6 +306,50 @@ func TestCreateAndDeleteInstance(t *testing.T) {
270306 // Verify nginx started successfully
271307 assert .True (t , foundNginxStartup , "Nginx should have started worker processes within 5 seconds" )
272308
309+ // Test volume is accessible from inside the guest via exec
310+ t .Log ("Testing volume from inside guest via exec..." )
311+
312+ // Helper to run command in guest
313+ runCmd := func (command ... string ) (string , int , error ) {
314+ var stdout , stderr bytes.Buffer
315+ exit , err := exec .ExecIntoInstance (ctx , inst .VsockSocket , exec.ExecOptions {
316+ Command : command ,
317+ Stdout : & stdout ,
318+ Stderr : & stderr ,
319+ TTY : false ,
320+ })
321+ if err != nil {
322+ return stderr .String (), - 1 , err
323+ }
324+ return strings .TrimSpace (stdout .String ()), exit .Code , nil
325+ }
326+
327+ // Verify volume mount point exists
328+ output , exitCode , err := runCmd ("ls" , "-la" , "/mnt/data" )
329+ require .NoError (t , err , "Should be able to ls /mnt/data" )
330+ assert .Equal (t , 0 , exitCode , "ls /mnt/data should succeed" )
331+ t .Logf ("Volume mount contents: %s" , output )
332+
333+ // Write a test file to the volume
334+ testContent := "hello-from-volume-test"
335+ output , exitCode , err = runCmd ("sh" , "-c" , fmt .Sprintf ("echo '%s' > /mnt/data/test.txt" , testContent ))
336+ require .NoError (t , err , "Should be able to write to volume" )
337+ assert .Equal (t , 0 , exitCode , "Write to volume should succeed" )
338+
339+ // Read the test file back
340+ output , exitCode , err = runCmd ("cat" , "/mnt/data/test.txt" )
341+ require .NoError (t , err , "Should be able to read from volume" )
342+ assert .Equal (t , 0 , exitCode , "Read from volume should succeed" )
343+ assert .Equal (t , testContent , output , "Volume content should match what was written" )
344+ t .Log ("Volume read/write test passed!" )
345+
346+ // Verify it's a real mount (not just a directory)
347+ output , exitCode , err = runCmd ("df" , "/mnt/data" )
348+ require .NoError (t , err , "Should be able to df /mnt/data" )
349+ assert .Equal (t , 0 , exitCode , "df /mnt/data should succeed" )
350+ assert .Contains (t , output , "/dev/vd" , "Volume should be mounted from a block device" )
351+ t .Logf ("Volume mount info: %s" , output )
352+
273353 // Test streaming logs with live updates
274354 t .Log ("Testing log streaming with live updates..." )
275355 streamCtx , streamCancel := context .WithCancel (ctx )
@@ -323,8 +403,23 @@ func TestCreateAndDeleteInstance(t *testing.T) {
323403 // Verify instance no longer exists
324404 _ , err = manager .GetInstance (ctx , inst .Id )
325405 assert .ErrorIs (t , err , ErrNotFound )
406+
407+ // Verify volume is detached but still exists
408+ vol , err = volumeManager .GetVolume (ctx , vol .Id )
409+ require .NoError (t , err )
410+ assert .Nil (t , vol .AttachedTo , "Volume should be detached after instance deletion" )
411+ assert .FileExists (t , p .VolumeData (vol .Id ), "Volume file should still exist" )
412+
413+ // Delete volume
414+ t .Log ("Deleting volume..." )
415+ err = volumeManager .DeleteVolume (ctx , vol .Id )
416+ require .NoError (t , err )
417+
418+ // Verify volume is gone
419+ _ , err = volumeManager .GetVolume (ctx , vol .Id )
420+ assert .ErrorIs (t , err , volumes .ErrNotFound )
326421
327- t .Log ("Instance lifecycle test complete!" )
422+ t .Log ("Instance and volume lifecycle test complete!" )
328423}
329424
330425func TestStorageOperations (t * testing.T ) {
0 commit comments