Skip to content

Commit

Permalink
xrootd: add truncate request
Browse files Browse the repository at this point in the history
Updates #170.
  • Loading branch information
EgorMatirov committed Jun 8, 2018
1 parent 7f037c8 commit 86ce88f
Show file tree
Hide file tree
Showing 10 changed files with 322 additions and 0 deletions.
7 changes: 7 additions & 0 deletions xrootd/client/file.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"go-hep.org/x/hep/xrootd/xrdfs"
"go-hep.org/x/hep/xrootd/xrdproto/read"
"go-hep.org/x/hep/xrootd/xrdproto/sync"
"go-hep.org/x/hep/xrootd/xrdproto/truncate"
"go-hep.org/x/hep/xrootd/xrdproto/write"
"go-hep.org/x/hep/xrootd/xrdproto/xrdclose"
)
Expand Down Expand Up @@ -87,6 +88,12 @@ func (f file) WriteAt(p []byte, off int64) (n int, err error) {
return len(p), nil
}

// Truncate changes the size of the named file.
func (f file) Truncate(ctx context.Context, size int64) error {
_, err := f.fs.c.call(ctx, &truncate.Request{Handle: f.handle, Size: size})
return err
}

var (
_ xrdfs.File = (*file)(nil)
)
62 changes: 62 additions & 0 deletions xrootd/client/file_mock_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"go-hep.org/x/hep/xrootd/xrdproto"
"go-hep.org/x/hep/xrootd/xrdproto/read"
"go-hep.org/x/hep/xrootd/xrdproto/sync"
"go-hep.org/x/hep/xrootd/xrdproto/truncate"
"go-hep.org/x/hep/xrootd/xrdproto/write"
"go-hep.org/x/hep/xrootd/xrdproto/xrdclose"
)
Expand Down Expand Up @@ -280,3 +281,64 @@ func TestFile_WriteAt_Mock(t *testing.T) {

testClientWithMockServer(serverFunc, clientFunc)
}

func TestFile_Truncate_Mock(t *testing.T) {
var (
handle = xrdfs.FileHandle{1, 2, 3, 4}
wantSize int64 = 10
)

wantRequest := truncate.Request{Handle: handle, Size: wantSize}

serverFunc := func(cancel func(), conn net.Conn) {
data, err := readRequest(conn)
if err != nil {
cancel()
t.Fatalf("could not read request: %v", err)
}

var gotRequest truncate.Request
gotHeader, err := unmarshalRequest(data, &gotRequest)
if err != nil {
cancel()
t.Fatalf("could not unmarshal request: %v", err)
}

if gotHeader.RequestID != wantRequest.ReqID() {
cancel()
t.Fatalf("invalid request id was specified:\nwant = %d\ngot = %d\n", wantRequest.ReqID(), gotHeader.RequestID)
}

if !reflect.DeepEqual(gotRequest, wantRequest) {
cancel()
t.Fatalf("request info does not match:\ngot = %v\nwant = %v", gotRequest, wantRequest)
}

responseHeader := xrdproto.ResponseHeader{
StreamID: gotHeader.StreamID,
Status: xrdproto.Ok,
}

responseData, err := marshalResponse(responseHeader)
if err != nil {
cancel()
t.Fatalf("could not marshal response header: %v", err)
}

if err := writeResponse(conn, responseData); err != nil {
cancel()
t.Fatalf("invalid write: %s", err)
}
}

clientFunc := func(cancel func(), client *Client) {
file := file{fs: client.FS().(*fileSystem), handle: handle}

err := file.Truncate(context.Background(), wantSize)
if err != nil {
t.Fatalf("invalid truncate call: %v", err)
}
}

testClientWithMockServer(serverFunc, clientFunc)
}
67 changes: 67 additions & 0 deletions xrootd/client/file_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -168,3 +168,70 @@ func TestFile_WriteAt(t *testing.T) {
})
}
}

func testFile_Truncate(t *testing.T, addr string) {
filePath := "/tmp/test_truncate.txt"
write := []uint8{1, 2, 3, 4, 5, 6, 7, 8}
want := write[:4]

client, err := NewClient(context.Background(), addr, "gopher")
if err != nil {
t.Fatalf("could not create client: %v", err)
}
defer client.Close()

fs := client.FS()
file, err := fs.Open(context.Background(), filePath, xrdfs.OpenModeOwnerWrite, xrdfs.OpenOptionsOpenUpdate)
if err != nil {
t.Fatalf("invalid open call: %v", err)
}
defer file.Close(context.Background())

_, err = file.WriteAt(write, 0)
if err != nil {
t.Fatalf("invalid write call: %v", err)
}

err = file.Truncate(context.Background(), int64(len(want)))
if err != nil {
t.Fatalf("invalid truncate call: %v", err)
}

err = file.Sync(context.Background())
if err != nil {
t.Fatalf("invalid sync call: %v", err)
}

err = file.Close(context.Background())
if err != nil {
t.Fatalf("invalid close call: %v", err)
}
file, err = fs.Open(context.Background(), filePath, xrdfs.OpenModeOwnerRead, xrdfs.OpenOptionsOpenRead)
if err != nil {
t.Fatalf("invalid open call: %v", err)
}
defer file.Close(context.Background())

got := make([]uint8, len(want)+10)
n, err := file.ReadAt(got, 0)
if err != nil {
t.Fatalf("invalid read call: %v", err)
}

if n != len(want) {
t.Fatalf("read count does not match:\ngot = %v\nwant = %v", n, len(want))
}

if !reflect.DeepEqual(got[:n], want) {
t.Fatalf("read data does not match:\ngot = %v\nwant = %v", got[:n], want)
}

}

func TestFile_Truncate(t *testing.T) {
for _, addr := range testClientAddrs {
t.Run(addr, func(t *testing.T) {
testFile_Truncate(t, addr)
})
}
}
7 changes: 7 additions & 0 deletions xrootd/client/filesystem.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"go-hep.org/x/hep/xrootd/xrdproto/dirlist"
"go-hep.org/x/hep/xrootd/xrdproto/open"
"go-hep.org/x/hep/xrootd/xrdproto/rm"
"go-hep.org/x/hep/xrootd/xrdproto/truncate"
)

// FS returns a xrdfs.FileSystem which uses this client to make requests.
Expand Down Expand Up @@ -49,6 +50,12 @@ func (fs *fileSystem) RemoveFile(ctx context.Context, path string) error {
return err
}

// Truncate changes the size of the named file.
func (fs *fileSystem) Truncate(ctx context.Context, path string, size int64) error {
_, err := fs.c.call(ctx, &truncate.Request{Path: path, Size: size})
return err
}

var (
_ xrdfs.FileSystem = (*fileSystem)(nil)
)
57 changes: 57 additions & 0 deletions xrootd/client/filesystem_mock_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
"go-hep.org/x/hep/xrootd/xrdproto/dirlist"
"go-hep.org/x/hep/xrootd/xrdproto/open"
"go-hep.org/x/hep/xrootd/xrdproto/rm"
"go-hep.org/x/hep/xrootd/xrdproto/truncate"
)

func TestFileSystem_Dirlist_Mock(t *testing.T) {
Expand Down Expand Up @@ -336,3 +337,59 @@ func TestFileSystem_RemoveFile_Mock(t *testing.T) {

testClientWithMockServer(serverFunc, clientFunc)
}

func TestFileSystem_Truncate_Mock(t *testing.T) {
var (
path = "/tmp/test"
wantSize int64 = 10
wantRequest = truncate.Request{Path: path, Size: wantSize}
)

serverFunc := func(cancel func(), conn net.Conn) {
data, err := readRequest(conn)
if err != nil {
cancel()
t.Fatalf("could not read request: %v", err)
}

var gotRequest truncate.Request
gotHeader, err := unmarshalRequest(data, &gotRequest)
if err != nil {
cancel()
t.Fatalf("could not unmarshal request: %v", err)
}

if gotHeader.RequestID != gotRequest.ReqID() {
cancel()
t.Fatalf("invalid request id was specified:\nwant = %d\ngot = %d\n", gotRequest.ReqID(), gotHeader.RequestID)
}

if !reflect.DeepEqual(gotRequest, wantRequest) {
cancel()
t.Fatalf("request info does not match:\ngot = %v\nwant = %v", gotRequest, wantRequest)
}

responseHeader := xrdproto.ResponseHeader{StreamID: gotHeader.StreamID}

responseData, err := marshalResponse(responseHeader)
if err != nil {
cancel()
t.Fatalf("could not marshal response: %v", err)
}

if err := writeResponse(conn, responseData); err != nil {
cancel()
t.Fatalf("invalid write: %s", err)
}
}

clientFunc := func(cancel func(), client *Client) {
fs := fileSystem{client}
err := fs.Truncate(context.Background(), path, wantSize)
if err != nil {
t.Fatalf("invalid truncate call: %v", err)
}
}

testClientWithMockServer(serverFunc, clientFunc)
}
68 changes: 68 additions & 0 deletions xrootd/client/filesystem_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -160,3 +160,71 @@ func TestFileSystem_RemoveFile(t *testing.T) {
})
}
}

func testFileSystem_Truncate(t *testing.T, addr string) {
filePath := "/tmp/test_truncate_fs.txt"
write := []uint8{1, 2, 3, 4, 5, 6, 7, 8}
want := write[:4]

client, err := NewClient(context.Background(), addr, "gopher")
if err != nil {
t.Fatalf("could not create client: %v", err)
}
defer client.Close()

fs := client.FS()
file, err := fs.Open(context.Background(), filePath, xrdfs.OpenModeOwnerWrite, xrdfs.OpenOptionsOpenUpdate)
if err != nil {
t.Fatalf("invalid open call: %v", err)
}
defer file.Close(context.Background())

_, err = file.WriteAt(write, 0)
if err != nil {
t.Fatalf("invalid write call: %v", err)
}

err = file.Sync(context.Background())
if err != nil {
t.Fatalf("invalid sync call: %v", err)
}

err = file.Close(context.Background())
if err != nil {
t.Fatalf("invalid close call: %v", err)
}

err = fs.Truncate(context.Background(), filePath, int64(len(want)))
if err != nil {
t.Fatalf("invalid truncate call: %v", err)
}

file, err = fs.Open(context.Background(), filePath, xrdfs.OpenModeOwnerRead, xrdfs.OpenOptionsOpenRead)
if err != nil {
t.Fatalf("invalid open call: %v", err)
}
defer file.Close(context.Background())

got := make([]uint8, len(want)+10)
n, err := file.ReadAt(got, 0)
if err != nil {
t.Fatalf("invalid read call: %v", err)
}

if n != len(want) {
t.Fatalf("read count does not match:\ngot = %v\nwant = %v", n, len(want))
}

if !reflect.DeepEqual(got[:n], want) {
t.Fatalf("read data does not match:\ngot = %v\nwant = %v", got[:n], want)
}

}

func TestFileSystem_Truncate(t *testing.T) {
for _, addr := range testClientAddrs {
t.Run(addr, func(t *testing.T) {
testFileSystem_Truncate(t, addr)
})
}
}
2 changes: 2 additions & 0 deletions xrootd/xrdfs/file.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ type File interface {
// WriteAtContext writes len(p) bytes from p to the file at offset off.
WriteAtContext(ctx context.Context, p []byte, off int64) error
io.WriterAt
// Truncate changes the size of the file.
Truncate(ctx context.Context, size int64) error
}

// FileHandle is the file handle, which should be treated as opaque data.
Expand Down
1 change: 1 addition & 0 deletions xrootd/xrdfs/fs.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ type FileSystem interface {
Dirlist(ctx context.Context, path string) ([]EntryStat, error)
Open(ctx context.Context, path string, mode OpenMode, options OpenOptions) (File, error)
RemoveFile(ctx context.Context, path string) error
Truncate(ctx context.Context, path string, size int64) error
}

// OpenMode is the mode in which path is to be opened.
Expand Down
5 changes: 5 additions & 0 deletions xrootd/xrdproto/signing.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"go-hep.org/x/hep/xrootd/xrdproto/open"
"go-hep.org/x/hep/xrootd/xrdproto/read"
"go-hep.org/x/hep/xrootd/xrdproto/rm"
"go-hep.org/x/hep/xrootd/xrdproto/truncate"
"go-hep.org/x/hep/xrootd/xrdproto/write"
"go-hep.org/x/hep/xrootd/xrdproto/xrdclose"
)
Expand Down Expand Up @@ -103,16 +104,19 @@ func NewSignRequirements(level SecurityLevel, overrides []SecurityOverride) Sign
// TODO: set requirements
sr.requirements[open.RequestID] = SignLikely
sr.requirements[rm.RequestID] = SignNeeded
sr.requirements[truncate.RequestID] = SignNeeded
}
if level >= Standard {
// TODO: set requirements
sr.requirements[open.RequestID] = SignNeeded
sr.requirements[rm.RequestID] = SignNeeded
sr.requirements[truncate.RequestID] = SignNeeded
}
if level >= Intense {
// TODO: set requirements
sr.requirements[xrdclose.RequestID] = SignNeeded
sr.requirements[open.RequestID] = SignNeeded
sr.requirements[truncate.RequestID] = SignNeeded
sr.requirements[write.RequestID] = SignNeeded
sr.requirements[rm.RequestID] = SignNeeded
}
Expand All @@ -122,6 +126,7 @@ func NewSignRequirements(level SecurityLevel, overrides []SecurityOverride) Sign
sr.requirements[dirlist.RequestID] = SignNeeded
sr.requirements[open.RequestID] = SignNeeded
sr.requirements[read.RequestID] = SignNeeded
sr.requirements[truncate.RequestID] = SignNeeded
sr.requirements[write.RequestID] = SignNeeded
sr.requirements[rm.RequestID] = SignNeeded
}
Expand Down
Loading

0 comments on commit 86ce88f

Please sign in to comment.