Skip to content

Commit

Permalink
api: add follow param to file stream endpoint
Browse files Browse the repository at this point in the history
The `/v1/client/fs/stream endpoint` supports tailing a file by writing
chunks out as they come in. But not all browsers support streams
(ex IE11) so we need to be able to tail a file without streaming.

The fs stream and logs endpoint use the same implementation for
filesystem streaming under the hood, but the fs stream always passes
the `follow` parameter set to true. This adds the same toggle to the
fs stream endpoint that we have for logs. It defaults to true for
backwards compatibility.
  • Loading branch information
tgross committed Jul 31, 2019
1 parent 19cdfb6 commit 293d212
Show file tree
Hide file tree
Showing 2 changed files with 49 additions and 2 deletions.
12 changes: 10 additions & 2 deletions command/agent/fs_endpoint.go
Original file line number Diff line number Diff line change
Expand Up @@ -194,11 +194,14 @@ func (s *HTTPServer) FileCatRequest(resp http.ResponseWriter, req *http.Request)
// Stream streams the content of a file blocking on EOF.
// The parameters are:
// * path: path to file to stream.
// * follow: A boolean of whether to follow the file, defaults to true.
// * offset: The offset to start streaming data at, defaults to zero.
// * origin: Either "start" or "end" and defines from where the offset is
// applied. Defaults to "start".
func (s *HTTPServer) Stream(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
var allocID, path string
var follow bool = true
var err error

q := req.URL.Query()

Expand All @@ -210,10 +213,15 @@ func (s *HTTPServer) Stream(resp http.ResponseWriter, req *http.Request) (interf
return nil, fileNameNotPresentErr
}

if followStr := q.Get("follow"); followStr != "" {
if follow, err = strconv.ParseBool(followStr); err != nil {
return nil, fmt.Errorf("Failed to parse follow field to boolean: %v", err)
}
}

var offset int64
offsetString := q.Get("offset")
if offsetString != "" {
var err error
if offset, err = strconv.ParseInt(offsetString, 10, 64); err != nil {
return nil, fmt.Errorf("error parsing offset: %v", err)
}
Expand All @@ -234,7 +242,7 @@ func (s *HTTPServer) Stream(resp http.ResponseWriter, req *http.Request) (interf
Path: path,
Origin: origin,
Offset: offset,
Follow: true,
Follow: follow,
}
s.parse(resp, req, &fsReq.QueryOptions.Region, &fsReq.QueryOptions)

Expand Down
39 changes: 39 additions & 0 deletions command/agent/fs_endpoint_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,45 @@ func TestHTTP_FS_Cat(t *testing.T) {
}

func TestHTTP_FS_Stream(t *testing.T) {
t.Parallel()
require := require.New(t)
httpTest(t, nil, func(s *TestAgent) {
a := mockFSAlloc(s.client.NodeID(), nil)
addAllocToClient(s, a, terminalClientAlloc)

offset := 4
expectation := base64.StdEncoding.EncodeToString(
[]byte(defaultLoggerMockDriverStdout[len(defaultLoggerMockDriverStdout)-offset:]))
path := fmt.Sprintf("/v1/client/fs/stream/%s?path=alloc/logs/web.stdout.0&offset=%d&origin=end&follow=false",
a.ID, offset)

p, _ := io.Pipe()
req, err := http.NewRequest("GET", path, p)
require.Nil(err)
respW := testutil.NewResponseRecorder()
go func() {
_, err = s.Server.Stream(respW, req)
require.Nil(err)
}()

out := ""
testutil.WaitForResult(func() (bool, error) {
output, err := ioutil.ReadAll(respW)
if err != nil {
return false, err
}

out += string(output)
return strings.Contains(out, expectation), fmt.Errorf("%q doesn't contain %q", out, expectation)
}, func(err error) {
t.Fatal(err)
})

p.Close()
})
}

func TestHTTP_FS_Stream_Follow(t *testing.T) {
t.Parallel()
require := require.New(t)
httpTest(t, nil, func(s *TestAgent) {
Expand Down

0 comments on commit 293d212

Please sign in to comment.