diff --git a/seaweed-volume/src/server/grpc_server.rs b/seaweed-volume/src/server/grpc_server.rs index 1020e971f..209fbb87e 100644 --- a/seaweed-volume/src/server/grpc_server.rs +++ b/seaweed-volume/src/server/grpc_server.rs @@ -3084,14 +3084,15 @@ impl VolumeServer for VolumeGrpcService { }; match store.read_volume_needle(vid, &mut n) { Ok(_) => { + let ttl_str = n.ttl.as_ref().map_or(String::new(), |t| t.to_string()); return Ok(Response::new( volume_server_pb::VolumeNeedleStatusResponse { needle_id: n.id.0, cookie: n.cookie.0, - size: n.data_size, + size: n.size.0 as u32, last_modified: n.last_modified, crc: n.checksum.0, - ttl: String::new(), + ttl: ttl_str, }, )) } diff --git a/test/volume_server/grpc/admin_extra_test.go b/test/volume_server/grpc/admin_extra_test.go index 32f349e81..9e860bfb3 100644 --- a/test/volume_server/grpc/admin_extra_test.go +++ b/test/volume_server/grpc/admin_extra_test.go @@ -2,6 +2,7 @@ package volume_server_grpc_test import ( "context" + "io" "net/http" "strings" "testing" @@ -57,6 +58,50 @@ func TestVolumeNeedleStatusForUploadedFile(t *testing.T) { } } +func TestVolumeNeedleStatusIncludesTtlAndLastModified(t *testing.T) { + if testing.Short() { + t.Skip("skipping integration test in short mode") + } + + clusterHarness := framework.StartVolumeCluster(t, matrix.P1()) + conn, grpcClient := framework.DialVolumeServer(t, clusterHarness.VolumeGRPCAddress()) + defer conn.Close() + + const volumeID = uint32(27) + const needleID = uint64(778901) + const cookie = uint32(0xA1B2C3D6) + framework.AllocateVolume(t, grpcClient, volumeID, "") + + fid := framework.NewFileID(volumeID, needleID, cookie) + client := framework.NewHTTPClient() + uploadReq := mustNewRequest(t, http.MethodPost, clusterHarness.VolumeAdminURL()+"/"+fid+"?ttl=7d&ts=1700000000") + uploadReq.Body = io.NopCloser(strings.NewReader("needle-status-ttl-payload")) + uploadReq.ContentLength = int64(len("needle-status-ttl-payload")) + uploadReq.Header.Set("Content-Type", "application/octet-stream") + uploadResp := framework.DoRequest(t, client, uploadReq) + _ = framework.ReadAllAndClose(t, uploadResp) + if uploadResp.StatusCode != http.StatusCreated { + t.Fatalf("upload status: expected 201, got %d", uploadResp.StatusCode) + } + + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + statusResp, err := grpcClient.VolumeNeedleStatus(ctx, &volume_server_pb.VolumeNeedleStatusRequest{ + VolumeId: volumeID, + NeedleId: needleID, + }) + if err != nil { + t.Fatalf("VolumeNeedleStatus with ttl failed: %v", err) + } + if statusResp.GetTtl() != "7d" { + t.Fatalf("ttl mismatch: got %q want %q", statusResp.GetTtl(), "7d") + } + if statusResp.GetLastModified() != 1700000000 { + t.Fatalf("last modified mismatch: got %d want %d", statusResp.GetLastModified(), 1700000000) + } +} + func TestVolumeNeedleStatusViaEcShardsWhenNormalVolumeUnmounted(t *testing.T) { if testing.Short() { t.Skip("skipping integration test in short mode")