Add post-terminate hook

This commit is contained in:
Marius 2016-03-12 22:24:57 +01:00
parent 5aa7928111
commit 3ee1c61d80
5 changed files with 72 additions and 18 deletions

4
.hooks/post-terminate Executable file
View File

@ -0,0 +1,4 @@
#!/bin/bash
echo "Upload $TUS_ID terminated"
cat /dev/stdin | jq .

View File

@ -134,10 +134,11 @@ func main() {
stdout.Printf("Using %.2fMB as maximum size.\n", float64(maxSize)/1024/1024)
handler, err := tusd.NewHandler(tusd.Config{
MaxSize: maxSize,
BasePath: basepath,
StoreComposer: composer,
NotifyCompleteUploads: true,
MaxSize: maxSize,
BasePath: basepath,
StoreComposer: composer,
NotifyCompleteUploads: true,
NotifyTerminatedUploads: true,
})
if err != nil {
stderr.Fatalf("Unable to create handler: %s", err)
@ -152,7 +153,9 @@ func main() {
for {
select {
case info := <-handler.CompleteUploads:
invokeHook(info)
invokeHook("post-finish", info)
case info := <-handler.TerminatedUploads:
invokeHook("post-terminate", info)
}
}
}()
@ -176,16 +179,21 @@ func main() {
}
}
func invokeHook(info tusd.FileInfo) {
stdout.Printf("Upload %s (%d bytes) finished\n", info.ID, info.Size)
func invokeHook(name string, info tusd.FileInfo) {
switch name {
case "post-finish":
stdout.Printf("Upload %s (%d bytes) finished\n", info.ID, info.Size)
case "post-terminate":
stdout.Printf("Upload %s terminated\n", info.ID)
}
if !hookInstalled {
return
}
stdout.Println("Invoking hooks…")
stdout.Printf("Invoking %s hook…\n", name)
cmd := exec.Command(hooksDir + "/post-finish")
cmd := exec.Command(hooksDir + "/" + name)
env := os.Environ()
env = append(env, "TUS_ID="+info.ID)
env = append(env, "TUS_SIZE="+strconv.FormatInt(info.Size, 10))
@ -206,7 +214,7 @@ func invokeHook(info tusd.FileInfo) {
go func() {
err := cmd.Run()
if err != nil {
stderr.Printf("Error running postfinish hook for %s: %s", info.ID, err)
stderr.Printf("Error running %s hook for %s: %s", name, info.ID, err)
}
}()
}

View File

@ -12,7 +12,7 @@ type Config struct {
// DataStore implementation used to store and retrieve the single uploads.
// The usage of this field is deprecated and should be avoided in favor of
// StoreComposer.
DataStore DataStore
DataStore DataStore
// StoreComposer points to the store composer from which the core data store
// and optional dependencies should be taken. May only be nil if DataStore is
// set.
@ -28,6 +28,9 @@ type Config struct {
// Initiate the CompleteUploads channel in the Handler struct in order to
// be notified about complete uploads
NotifyCompleteUploads bool
// NotifyTerminatedUploads indicates whether sending notifications about
// terminated uploads using the TerminatedUploads channel should be enabled.
NotifyTerminatedUploads bool
// Logger the logger to use internally
Logger *log.Logger
// Respect the X-Forwarded-Host, X-Forwarded-Proto and Forwarded headers

View File

@ -5,6 +5,8 @@ import (
"testing"
. "github.com/tus/tusd"
"github.com/stretchr/testify/assert"
)
type terminateStore struct {
@ -12,6 +14,13 @@ type terminateStore struct {
zeroStore
}
func (s terminateStore) GetInfo(id string) (FileInfo, error) {
return FileInfo{
ID: id,
Size: 10,
}, nil
}
func (s terminateStore) Terminate(id string) error {
if id != "foo" {
s.t.Fatal("unexpected id")
@ -24,8 +33,12 @@ func TestTerminate(t *testing.T) {
DataStore: terminateStore{
t: t,
},
NotifyTerminatedUploads: true,
})
c := make(chan FileInfo, 1)
handler.TerminatedUploads = c
(&httpTest{
Name: "Successful OPTIONS request",
Method: "OPTIONS",
@ -45,6 +58,12 @@ func TestTerminate(t *testing.T) {
},
Code: http.StatusNoContent,
}).Run(handler, t)
info := <-c
a := assert.New(t)
a.Equal("foo", info.ID)
a.Equal(int64(10), info.Size)
}
func TestTerminateNotImplemented(t *testing.T) {

View File

@ -66,6 +66,12 @@ type UnroutedHandler struct {
// this unbuffered channel. The NotifyCompleteUploads property in the Config
// struct must be set to true in order to work.
CompleteUploads chan FileInfo
// TerminatedUploads is used to send notifications whenever an upload is
// terminated by a user. The FileInfo will contain information about This
// upload gathered before the termination. Sending to this channel will only
// happen if the NotifyTerminatedUploads field is set to true in the Config
// structure.
TerminatedUploads chan FileInfo
}
// NewUnroutedHandler creates a new handler without routing using the given
@ -87,13 +93,14 @@ func NewUnroutedHandler(config Config) (*UnroutedHandler, error) {
}
handler := &UnroutedHandler{
config: config,
composer: config.StoreComposer,
basePath: config.BasePath,
isBasePathAbs: config.isAbs,
CompleteUploads: make(chan FileInfo),
logger: config.Logger,
extensions: extensions,
config: config,
composer: config.StoreComposer,
basePath: config.BasePath,
isBasePathAbs: config.isAbs,
CompleteUploads: make(chan FileInfo),
TerminatedUploads: make(chan FileInfo),
logger: config.Logger,
extensions: extensions,
}
return handler, nil
@ -461,6 +468,15 @@ func (handler *UnroutedHandler) DelFile(w http.ResponseWriter, r *http.Request)
defer locker.UnlockUpload(id)
}
var info FileInfo
if handler.config.NotifyTerminatedUploads {
info, err = handler.composer.Core.GetInfo(id)
if err != nil {
handler.sendError(w, r, err)
return
}
}
err = handler.composer.Terminater.Terminate(id)
if err != nil {
handler.sendError(w, r, err)
@ -468,6 +484,10 @@ func (handler *UnroutedHandler) DelFile(w http.ResponseWriter, r *http.Request)
}
w.WriteHeader(http.StatusNoContent)
if handler.config.NotifyTerminatedUploads {
handler.TerminatedUploads <- info
}
}
// Send the error in the response body. The status code will be looked up in