From 5afe3f7f7ad2942375270267817113ca00f6039a Mon Sep 17 00:00:00 2001 From: talwai Date: Wed, 19 Aug 2015 18:06:20 -0400 Subject: [PATCH 1/2] [filesystem] better signal handling for timeout --- filesystem/filesystem.go | 41 ++++++++++++++++++++++------------------ 1 file changed, 23 insertions(+), 18 deletions(-) diff --git a/filesystem/filesystem.go b/filesystem/filesystem.go index cc05beb..eaeb414 100644 --- a/filesystem/filesystem.go +++ b/filesystem/filesystem.go @@ -15,8 +15,8 @@ func getFileSystemInfo() (interface{}, error) { /* Grab filesystem data from df */ cmd := exec.Command("df", dfOptions...) - outCh := make(chan []byte) - errCh := make(chan error) + outCh := make(chan []byte, 1) + errCh := make(chan error, 1) var out interface{} var err error @@ -24,28 +24,33 @@ func getFileSystemInfo() (interface{}, error) { go func() { _out, _err := cmd.Output() if _err != nil { - errCh <- _err + errCh <- fmt.Errorf("df failed to collect filesystem data: %s", _err) return } outCh <- _out }() - select { - case res := <-outCh: - if res != nil { - out, err = parseDfOutput(string(res)) - } else { - out, err = nil, fmt.Errorf("df process timed out and was killed!") +WAIT: + for { + select { + case res := <-outCh: + if res != nil { + out, err = parseDfOutput(string(res)) + } else { + out, err = nil, fmt.Errorf("df failed to collect filesystem data") + } + break WAIT + case err = <-errCh: + out = nil + break WAIT + case <-time.After(2 * time.Second): + // Kill the process if it takes too long + if killErr := cmd.Process.Kill(); killErr != nil { + log.Fatal("failed to kill:", killErr) + // Force goroutine to exit + <-outCh + } } - case err = <-errCh: - out = nil - case <-time.After(5 * time.Second): - // Kill the process if it takes too long - if killErr := cmd.Process.Kill(); killErr != nil { - log.Fatal("failed to kill:", killErr) - } - //Let goroutine exit - <-outCh } return out, err From da80d6ebedd9db40c60765bb7f123af296eea853 Mon Sep 17 00:00:00 2001 From: talwai Date: Fri, 21 Aug 2015 10:48:29 -0400 Subject: [PATCH 2/2] [filesystem] add test for timeout --- filesystem/filesystem_test.go | 72 +++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 filesystem/filesystem_test.go diff --git a/filesystem/filesystem_test.go b/filesystem/filesystem_test.go new file mode 100644 index 0000000..ea3ba47 --- /dev/null +++ b/filesystem/filesystem_test.go @@ -0,0 +1,72 @@ +package filesystem + +import ( + "fmt" + "log" + "os/exec" + "reflect" + "testing" + "time" +) + +func MockSlowGetFileSystemInfo() (interface{}, error) { + /* Run a command that will definitely time out */ + cmd := exec.Command("sleep", "5") + + outCh := make(chan []byte, 1) + errCh := make(chan error, 1) + + var out interface{} + var err error + + go func() { + _out, _err := cmd.Output() + if _err != nil { + errCh <- fmt.Errorf("df failed to collect filesystem data: %s", _err) + return + } + outCh <- _out + }() + +WAIT: + for { + select { + case res := <-outCh: + if res != nil { + out, err = parseDfOutput(string(res)) + } else { + out, err = nil, fmt.Errorf("df failed to collect filesystem data") + } + break WAIT + case err = <-errCh: + out = nil + break WAIT + case <-time.After(2 * time.Second): + // Kill the process if it takes too long + if killErr := cmd.Process.Kill(); killErr != nil { + log.Fatal("failed to kill:", killErr) + // Force goroutine to exit + <-outCh + } + } + } + + return out, err +} + +func TestSlowGetFileSystemInfo(t *testing.T) { + out, err := MockSlowGetFileSystemInfo() + if !reflect.DeepEqual(out, nil) { + t.Fatalf("Failed! out should be nil. Instead it's %s", out) + } + if !reflect.DeepEqual(err, fmt.Errorf("df failed to collect filesystem data: signal: killed")) { + t.Fatalf("Failed! Wrong error: %s", err) + } +} + +func TestGetFileSystemInfo(t *testing.T) { + _, err := getFileSystemInfo() + if !reflect.DeepEqual(err, nil) { + t.Fatalf("getFileSystemInfo failed: %s", err) + } +}