Skip to content

Commit

Permalink
filestore: Expose .info path and fix absolute paths (#1162)
Browse files Browse the repository at this point in the history
* filestore: Allow providing custom, absolute paths

* filestore: Expose path to `.info` file in hooks
  • Loading branch information
Acconut authored Jul 29, 2024
1 parent 585d7a5 commit b3ba87f
Show file tree
Hide file tree
Showing 3 changed files with 64 additions and 9 deletions.
4 changes: 3 additions & 1 deletion docs/_advanced-topics/hooks.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,9 +83,11 @@ Below you can find an annotated, JSON-ish encoded example of a hook request:
// Storage contains information about where the upload is stored. The exact values
// depend on the storage that is used and are not available in the pre-create hook.
"Storage": {
// For example, the filestore supplies the absolute file path:
// For example, the filestore supplies the absolute file paths where the upload data
// (Path) and the associated info file (InfoPath) are stored:
"Type": "filestore",
"Path": "/my/upload/directory/14b1c4c77771671a8479bc0444bbc5ce",
"InfoPath": "/my/upload/directory/14b1c4c77771671a8479bc0444bbc5ce.info",

// The S3Store and GCSStore supply the bucket name and object key:
"Type": "s3store",
Expand Down
14 changes: 11 additions & 3 deletions pkg/filestore/filestore.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,14 +62,22 @@ func (store FileStore) NewUpload(ctx context.Context, info handler.FileInfo) (ha
// The binary file's location might be modified by the pre-create hook.
var binPath string
if info.Storage != nil && info.Storage["Path"] != "" {
binPath = filepath.Join(store.Path, info.Storage["Path"])
// filepath.Join treats absolute and relative paths the same, so we must
// handle them on our own. Absolute paths get used as-is, while relative
// paths are joined to the storage path.
if filepath.IsAbs(info.Storage["Path"]) {
binPath = info.Storage["Path"]
} else {
binPath = filepath.Join(store.Path, info.Storage["Path"])
}
} else {
binPath = store.defaultBinPath(info.ID)
}

info.Storage = map[string]string{
"Type": "filestore",
"Path": binPath,
"Type": "filestore",
"Path": binPath,
"InfoPath": infoPath,
}

// Create binary file with no content
Expand Down
55 changes: 50 additions & 5 deletions pkg/filestore/filestore_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,10 @@ func TestFilestore(t *testing.T) {
a.EqualValues(42, info.Size)
a.EqualValues(0, info.Offset)
a.Equal(handler.MetaData{"hello": "world"}, info.MetaData)
a.Equal(2, len(info.Storage))
a.Equal(3, len(info.Storage))
a.Equal("filestore", info.Storage["Type"])
a.Equal(filepath.Join(tmp, info.ID), info.Storage["Path"])
a.Equal(filepath.Join(tmp, info.ID+".info"), info.Storage["InfoPath"])

// Write data to upload
bytesWritten, err := upload.WriteChunk(ctx, 0, strings.NewReader("hello world"))
Expand Down Expand Up @@ -104,9 +105,10 @@ func TestCreateDirectories(t *testing.T) {
a.EqualValues(42, info.Size)
a.EqualValues(0, info.Offset)
a.Equal(handler.MetaData{"hello": "world"}, info.MetaData)
a.Equal(2, len(info.Storage))
a.Equal(3, len(info.Storage))
a.Equal("filestore", info.Storage["Type"])
a.Equal(filepath.Join(tmp, info.ID), info.Storage["Path"])
a.Equal(filepath.Join(tmp, info.ID+".info"), info.Storage["InfoPath"])

// Write data to upload
bytesWritten, err := upload.WriteChunk(ctx, 0, strings.NewReader("hello world"))
Expand Down Expand Up @@ -246,8 +248,9 @@ func TestDeclareLength(t *testing.T) {
a.Equal(false, updatedInfo.SizeIsDeferred)
}

// TestCustomPath tests whether the upload's destination can be customized.
func TestCustomPath(t *testing.T) {
// TestCustomRelativePath tests whether the upload's destination can be customized
// relative to the storage directory.
func TestCustomRelativePath(t *testing.T) {
a := assert.New(t)

tmp, err := os.MkdirTemp("", "tusd-filestore-")
Expand All @@ -272,9 +275,10 @@ func TestCustomPath(t *testing.T) {
a.NoError(err)
a.EqualValues(42, info.Size)
a.EqualValues(0, info.Offset)
a.Equal(2, len(info.Storage))
a.Equal(3, len(info.Storage))
a.Equal("filestore", info.Storage["Type"])
a.Equal(filepath.Join(tmp, "./folder2/bin"), info.Storage["Path"])
a.Equal(filepath.Join(tmp, "./folder1/info.info"), info.Storage["InfoPath"])

// Write data to upload
bytesWritten, err := upload.WriteChunk(ctx, 0, strings.NewReader("hello world"))
Expand Down Expand Up @@ -313,3 +317,44 @@ func TestCustomPath(t *testing.T) {
a.Equal(nil, upload)
a.Equal(handler.ErrNotFound, err)
}

// TestCustomAbsolutePath tests whether the upload's destination can be customized
// using an absolute path to the storage directory.
func TestCustomAbsolutePath(t *testing.T) {
a := assert.New(t)

tmp1, err := os.MkdirTemp("", "tusd-filestore-")
a.NoError(err)

tmp2, err := os.MkdirTemp("", "tusd-filestore-")
a.NoError(err)

store := FileStore{tmp1}
ctx := context.Background()

// Create new upload, but the Path property points to a directory
// outside of the directory given to FileStore
binPath := filepath.Join(tmp2, "dir/my-upload.bin")
upload, err := store.NewUpload(ctx, handler.FileInfo{
ID: "my-upload",
Size: 42,
Storage: map[string]string{
"Path": binPath,
},
})
a.NoError(err)
a.NotEqual(nil, upload)

info, err := upload.GetInfo(ctx)
a.NoError(err)
a.EqualValues(42, info.Size)
a.EqualValues(0, info.Offset)
a.Equal(3, len(info.Storage))
a.Equal("filestore", info.Storage["Type"])
a.Equal(binPath, info.Storage["Path"])
a.Equal(filepath.Join(tmp1, "my-upload.info"), info.Storage["InfoPath"])

statInfo, err := os.Stat(binPath)
a.NoError(err)
a.True(statInfo.Mode().IsRegular())
}

0 comments on commit b3ba87f

Please sign in to comment.