github.com/zhizhiboom/nomad@v0.8.5-0.20180907175415-f28fd3a1a056/client/fs_endpoint_test.go (about)

     1  package client
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"io"
     7  	"io/ioutil"
     8  	"math"
     9  	"net"
    10  	"os"
    11  	"path/filepath"
    12  	"reflect"
    13  	"strings"
    14  	"testing"
    15  	"time"
    16  
    17  	"github.com/hashicorp/nomad/acl"
    18  	"github.com/hashicorp/nomad/client/allocdir"
    19  	"github.com/hashicorp/nomad/client/config"
    20  	sframer "github.com/hashicorp/nomad/client/lib/streamframer"
    21  	cstructs "github.com/hashicorp/nomad/client/structs"
    22  	"github.com/hashicorp/nomad/helper/testlog"
    23  	"github.com/hashicorp/nomad/helper/uuid"
    24  	"github.com/hashicorp/nomad/nomad"
    25  	"github.com/hashicorp/nomad/nomad/mock"
    26  	"github.com/hashicorp/nomad/nomad/structs"
    27  	"github.com/hashicorp/nomad/testutil"
    28  	"github.com/stretchr/testify/require"
    29  	"github.com/ugorji/go/codec"
    30  )
    31  
    32  // tempAllocDir returns a new alloc dir that is rooted in a temp dir. The caller
    33  // should destroy the temp dir.
    34  func tempAllocDir(t testing.TB) *allocdir.AllocDir {
    35  	dir, err := ioutil.TempDir("", "")
    36  	if err != nil {
    37  		t.Fatalf("TempDir() failed: %v", err)
    38  	}
    39  
    40  	if err := os.Chmod(dir, 0777); err != nil {
    41  		t.Fatalf("failed to chmod dir: %v", err)
    42  	}
    43  
    44  	return allocdir.NewAllocDir(testlog.Logger(t), dir)
    45  }
    46  
    47  type nopWriteCloser struct {
    48  	io.Writer
    49  }
    50  
    51  func (n nopWriteCloser) Close() error {
    52  	return nil
    53  }
    54  
    55  func TestFS_Stat_NoAlloc(t *testing.T) {
    56  	t.Parallel()
    57  	require := require.New(t)
    58  
    59  	// Start a client
    60  	c := TestClient(t, nil)
    61  	defer c.Shutdown()
    62  
    63  	// Make the request with bad allocation id
    64  	req := &cstructs.FsStatRequest{
    65  		AllocID:      uuid.Generate(),
    66  		Path:         "foo",
    67  		QueryOptions: structs.QueryOptions{Region: "global"},
    68  	}
    69  
    70  	var resp cstructs.FsStatResponse
    71  	err := c.ClientRPC("FileSystem.Stat", req, &resp)
    72  	require.NotNil(err)
    73  	require.True(structs.IsErrUnknownAllocation(err))
    74  }
    75  
    76  func TestFS_Stat(t *testing.T) {
    77  	t.Parallel()
    78  	require := require.New(t)
    79  
    80  	// Start a client
    81  	c := TestClient(t, nil)
    82  	defer c.Shutdown()
    83  
    84  	// Create and add an alloc
    85  	a := mock.Alloc()
    86  	task := a.Job.TaskGroups[0].Tasks[0]
    87  	task.Driver = "mock_driver"
    88  	task.Config["run_for"] = "500ms"
    89  	c.addAlloc(a, "")
    90  
    91  	// Wait for the client to start it
    92  	testutil.WaitForResult(func() (bool, error) {
    93  		ar, ok := c.allocs[a.ID]
    94  		if !ok {
    95  			return false, fmt.Errorf("alloc doesn't exist")
    96  		}
    97  
    98  		alloc := ar.Alloc()
    99  		running := false
   100  		for _, s := range alloc.TaskStates {
   101  			if s.State == structs.TaskStateRunning {
   102  				running = true
   103  			} else {
   104  				running = false
   105  			}
   106  		}
   107  
   108  		return running, fmt.Errorf("tasks not running")
   109  	}, func(err error) {
   110  		t.Fatal(err)
   111  	})
   112  
   113  	// Make the request with bad allocation id
   114  	req := &cstructs.FsStatRequest{
   115  		AllocID:      a.ID,
   116  		Path:         "/",
   117  		QueryOptions: structs.QueryOptions{Region: "global"},
   118  	}
   119  
   120  	var resp cstructs.FsStatResponse
   121  	err := c.ClientRPC("FileSystem.Stat", req, &resp)
   122  	require.Nil(err)
   123  	require.NotNil(resp.Info)
   124  	require.True(resp.Info.IsDir)
   125  }
   126  
   127  func TestFS_Stat_ACL(t *testing.T) {
   128  	t.Parallel()
   129  	require := require.New(t)
   130  
   131  	// Start a server
   132  	s, root := nomad.TestACLServer(t, nil)
   133  	defer s.Shutdown()
   134  	testutil.WaitForLeader(t, s.RPC)
   135  
   136  	client := TestClient(t, func(c *config.Config) {
   137  		c.ACLEnabled = true
   138  		c.Servers = []string{s.GetConfig().RPCAddr.String()}
   139  	})
   140  	defer client.Shutdown()
   141  
   142  	// Create a bad token
   143  	policyBad := mock.NamespacePolicy("other", "", []string{acl.NamespaceCapabilityDeny})
   144  	tokenBad := mock.CreatePolicyAndToken(t, s.State(), 1005, "invalid", policyBad)
   145  
   146  	policyGood := mock.NamespacePolicy(structs.DefaultNamespace, "",
   147  		[]string{acl.NamespaceCapabilityReadLogs, acl.NamespaceCapabilityReadFS})
   148  	tokenGood := mock.CreatePolicyAndToken(t, s.State(), 1009, "valid2", policyGood)
   149  
   150  	cases := []struct {
   151  		Name          string
   152  		Token         string
   153  		ExpectedError string
   154  	}{
   155  		{
   156  			Name:          "bad token",
   157  			Token:         tokenBad.SecretID,
   158  			ExpectedError: structs.ErrPermissionDenied.Error(),
   159  		},
   160  		{
   161  			Name:          "good token",
   162  			Token:         tokenGood.SecretID,
   163  			ExpectedError: structs.ErrUnknownAllocationPrefix,
   164  		},
   165  		{
   166  			Name:          "root token",
   167  			Token:         root.SecretID,
   168  			ExpectedError: structs.ErrUnknownAllocationPrefix,
   169  		},
   170  	}
   171  
   172  	for _, c := range cases {
   173  		t.Run(c.Name, func(t *testing.T) {
   174  			// Make the request with bad allocation id
   175  			req := &cstructs.FsStatRequest{
   176  				AllocID: uuid.Generate(),
   177  				Path:    "/",
   178  				QueryOptions: structs.QueryOptions{
   179  					Region:    "global",
   180  					AuthToken: c.Token,
   181  					Namespace: structs.DefaultNamespace,
   182  				},
   183  			}
   184  
   185  			var resp cstructs.FsStatResponse
   186  			err := client.ClientRPC("FileSystem.Stat", req, &resp)
   187  			require.NotNil(err)
   188  			require.Contains(err.Error(), c.ExpectedError)
   189  		})
   190  	}
   191  }
   192  
   193  func TestFS_List_NoAlloc(t *testing.T) {
   194  	t.Parallel()
   195  	require := require.New(t)
   196  
   197  	// Start a client
   198  	c := TestClient(t, nil)
   199  	defer c.Shutdown()
   200  
   201  	// Make the request with bad allocation id
   202  	req := &cstructs.FsListRequest{
   203  		AllocID:      uuid.Generate(),
   204  		Path:         "foo",
   205  		QueryOptions: structs.QueryOptions{Region: "global"},
   206  	}
   207  
   208  	var resp cstructs.FsListResponse
   209  	err := c.ClientRPC("FileSystem.List", req, &resp)
   210  	require.NotNil(err)
   211  	require.True(structs.IsErrUnknownAllocation(err))
   212  }
   213  
   214  func TestFS_List(t *testing.T) {
   215  	t.Parallel()
   216  	require := require.New(t)
   217  
   218  	// Start a client
   219  	c := TestClient(t, nil)
   220  	defer c.Shutdown()
   221  
   222  	// Create and add an alloc
   223  	a := mock.Alloc()
   224  	task := a.Job.TaskGroups[0].Tasks[0]
   225  	task.Driver = "mock_driver"
   226  	task.Config["run_for"] = "500ms"
   227  	c.addAlloc(a, "")
   228  
   229  	// Wait for the client to start it
   230  	testutil.WaitForResult(func() (bool, error) {
   231  		ar, ok := c.allocs[a.ID]
   232  		if !ok {
   233  			return false, fmt.Errorf("alloc doesn't exist")
   234  		}
   235  
   236  		alloc := ar.Alloc()
   237  		running := false
   238  		for _, s := range alloc.TaskStates {
   239  			if s.State == structs.TaskStateRunning {
   240  				running = true
   241  			} else {
   242  				running = false
   243  			}
   244  		}
   245  
   246  		return running, fmt.Errorf("tasks not running")
   247  	}, func(err error) {
   248  		t.Fatal(err)
   249  	})
   250  
   251  	// Make the request
   252  	req := &cstructs.FsListRequest{
   253  		AllocID:      a.ID,
   254  		Path:         "/",
   255  		QueryOptions: structs.QueryOptions{Region: "global"},
   256  	}
   257  
   258  	var resp cstructs.FsListResponse
   259  	err := c.ClientRPC("FileSystem.List", req, &resp)
   260  	require.Nil(err)
   261  	require.NotEmpty(resp.Files)
   262  	require.True(resp.Files[0].IsDir)
   263  }
   264  
   265  func TestFS_List_ACL(t *testing.T) {
   266  	t.Parallel()
   267  	require := require.New(t)
   268  
   269  	// Start a server
   270  	s, root := nomad.TestACLServer(t, nil)
   271  	defer s.Shutdown()
   272  	testutil.WaitForLeader(t, s.RPC)
   273  
   274  	client := TestClient(t, func(c *config.Config) {
   275  		c.ACLEnabled = true
   276  		c.Servers = []string{s.GetConfig().RPCAddr.String()}
   277  	})
   278  	defer client.Shutdown()
   279  
   280  	// Create a bad token
   281  	policyBad := mock.NamespacePolicy("other", "", []string{acl.NamespaceCapabilityDeny})
   282  	tokenBad := mock.CreatePolicyAndToken(t, s.State(), 1005, "invalid", policyBad)
   283  
   284  	policyGood := mock.NamespacePolicy(structs.DefaultNamespace, "",
   285  		[]string{acl.NamespaceCapabilityReadLogs, acl.NamespaceCapabilityReadFS})
   286  	tokenGood := mock.CreatePolicyAndToken(t, s.State(), 1009, "valid2", policyGood)
   287  
   288  	cases := []struct {
   289  		Name          string
   290  		Token         string
   291  		ExpectedError string
   292  	}{
   293  		{
   294  			Name:          "bad token",
   295  			Token:         tokenBad.SecretID,
   296  			ExpectedError: structs.ErrPermissionDenied.Error(),
   297  		},
   298  		{
   299  			Name:          "good token",
   300  			Token:         tokenGood.SecretID,
   301  			ExpectedError: structs.ErrUnknownAllocationPrefix,
   302  		},
   303  		{
   304  			Name:          "root token",
   305  			Token:         root.SecretID,
   306  			ExpectedError: structs.ErrUnknownAllocationPrefix,
   307  		},
   308  	}
   309  
   310  	for _, c := range cases {
   311  		t.Run(c.Name, func(t *testing.T) {
   312  			// Make the request with bad allocation id
   313  			req := &cstructs.FsListRequest{
   314  				AllocID: uuid.Generate(),
   315  				Path:    "/",
   316  				QueryOptions: structs.QueryOptions{
   317  					Region:    "global",
   318  					AuthToken: c.Token,
   319  					Namespace: structs.DefaultNamespace,
   320  				},
   321  			}
   322  
   323  			var resp cstructs.FsListResponse
   324  			err := client.ClientRPC("FileSystem.List", req, &resp)
   325  			require.NotNil(err)
   326  			require.Contains(err.Error(), c.ExpectedError)
   327  		})
   328  	}
   329  }
   330  
   331  func TestFS_Stream_NoAlloc(t *testing.T) {
   332  	t.Parallel()
   333  	require := require.New(t)
   334  
   335  	// Start a client
   336  	c := TestClient(t, nil)
   337  	defer c.Shutdown()
   338  
   339  	// Make the request with bad allocation id
   340  	req := &cstructs.FsStreamRequest{
   341  		AllocID:      uuid.Generate(),
   342  		Path:         "foo",
   343  		Origin:       "start",
   344  		QueryOptions: structs.QueryOptions{Region: "global"},
   345  	}
   346  
   347  	// Get the handler
   348  	handler, err := c.StreamingRpcHandler("FileSystem.Stream")
   349  	require.Nil(err)
   350  
   351  	// Create a pipe
   352  	p1, p2 := net.Pipe()
   353  	defer p1.Close()
   354  	defer p2.Close()
   355  
   356  	errCh := make(chan error)
   357  	streamMsg := make(chan *cstructs.StreamErrWrapper)
   358  
   359  	// Start the handler
   360  	go handler(p2)
   361  
   362  	// Start the decoder
   363  	go func() {
   364  		decoder := codec.NewDecoder(p1, structs.MsgpackHandle)
   365  		for {
   366  			var msg cstructs.StreamErrWrapper
   367  			if err := decoder.Decode(&msg); err != nil {
   368  				if err == io.EOF || strings.Contains(err.Error(), "closed") {
   369  					return
   370  				}
   371  				errCh <- fmt.Errorf("error decoding: %v", err)
   372  			}
   373  
   374  			streamMsg <- &msg
   375  		}
   376  	}()
   377  
   378  	// Send the request
   379  	encoder := codec.NewEncoder(p1, structs.MsgpackHandle)
   380  	require.Nil(encoder.Encode(req))
   381  
   382  	timeout := time.After(3 * time.Second)
   383  
   384  OUTER:
   385  	for {
   386  		select {
   387  		case <-timeout:
   388  			t.Fatal("timeout")
   389  		case err := <-errCh:
   390  			t.Fatal(err)
   391  		case msg := <-streamMsg:
   392  			t.Logf("Got msg %+v", msg)
   393  			if msg.Error == nil {
   394  				continue
   395  			}
   396  
   397  			if structs.IsErrUnknownAllocation(msg.Error) {
   398  				break OUTER
   399  			} else {
   400  				t.Fatalf("bad error: %v", err)
   401  			}
   402  		}
   403  	}
   404  }
   405  
   406  func TestFS_Stream_ACL(t *testing.T) {
   407  	t.Parallel()
   408  	require := require.New(t)
   409  
   410  	// Start a server
   411  	s, root := nomad.TestACLServer(t, nil)
   412  	defer s.Shutdown()
   413  	testutil.WaitForLeader(t, s.RPC)
   414  
   415  	client := TestClient(t, func(c *config.Config) {
   416  		c.ACLEnabled = true
   417  		c.Servers = []string{s.GetConfig().RPCAddr.String()}
   418  	})
   419  	defer client.Shutdown()
   420  
   421  	// Create a bad token
   422  	policyBad := mock.NamespacePolicy("other", "", []string{acl.NamespaceCapabilityReadFS})
   423  	tokenBad := mock.CreatePolicyAndToken(t, s.State(), 1005, "invalid", policyBad)
   424  
   425  	policyGood := mock.NamespacePolicy(structs.DefaultNamespace, "",
   426  		[]string{acl.NamespaceCapabilityReadLogs, acl.NamespaceCapabilityReadFS})
   427  	tokenGood := mock.CreatePolicyAndToken(t, s.State(), 1009, "valid2", policyGood)
   428  
   429  	cases := []struct {
   430  		Name          string
   431  		Token         string
   432  		ExpectedError string
   433  	}{
   434  		{
   435  			Name:          "bad token",
   436  			Token:         tokenBad.SecretID,
   437  			ExpectedError: structs.ErrPermissionDenied.Error(),
   438  		},
   439  		{
   440  			Name:          "good token",
   441  			Token:         tokenGood.SecretID,
   442  			ExpectedError: structs.ErrUnknownAllocationPrefix,
   443  		},
   444  		{
   445  			Name:          "root token",
   446  			Token:         root.SecretID,
   447  			ExpectedError: structs.ErrUnknownAllocationPrefix,
   448  		},
   449  	}
   450  
   451  	for _, c := range cases {
   452  		t.Run(c.Name, func(t *testing.T) {
   453  			// Make the request with bad allocation id
   454  			req := &cstructs.FsStreamRequest{
   455  				AllocID: uuid.Generate(),
   456  				Path:    "foo",
   457  				Origin:  "start",
   458  				QueryOptions: structs.QueryOptions{
   459  					Namespace: structs.DefaultNamespace,
   460  					Region:    "global",
   461  					AuthToken: c.Token,
   462  				},
   463  			}
   464  
   465  			// Get the handler
   466  			handler, err := client.StreamingRpcHandler("FileSystem.Stream")
   467  			require.Nil(err)
   468  
   469  			// Create a pipe
   470  			p1, p2 := net.Pipe()
   471  			defer p1.Close()
   472  			defer p2.Close()
   473  
   474  			errCh := make(chan error)
   475  			streamMsg := make(chan *cstructs.StreamErrWrapper)
   476  
   477  			// Start the handler
   478  			go handler(p2)
   479  
   480  			// Start the decoder
   481  			go func() {
   482  				decoder := codec.NewDecoder(p1, structs.MsgpackHandle)
   483  				for {
   484  					var msg cstructs.StreamErrWrapper
   485  					if err := decoder.Decode(&msg); err != nil {
   486  						if err == io.EOF || strings.Contains(err.Error(), "closed") {
   487  							return
   488  						}
   489  						errCh <- fmt.Errorf("error decoding: %v", err)
   490  					}
   491  
   492  					streamMsg <- &msg
   493  				}
   494  			}()
   495  
   496  			// Send the request
   497  			encoder := codec.NewEncoder(p1, structs.MsgpackHandle)
   498  			require.Nil(encoder.Encode(req))
   499  
   500  			timeout := time.After(5 * time.Second)
   501  
   502  		OUTER:
   503  			for {
   504  				select {
   505  				case <-timeout:
   506  					t.Fatal("timeout")
   507  				case err := <-errCh:
   508  					t.Fatal(err)
   509  				case msg := <-streamMsg:
   510  					if msg.Error == nil {
   511  						continue
   512  					}
   513  
   514  					if strings.Contains(msg.Error.Error(), c.ExpectedError) {
   515  						break OUTER
   516  					} else {
   517  						t.Fatalf("Bad error: %v", msg.Error)
   518  					}
   519  				}
   520  			}
   521  		})
   522  	}
   523  }
   524  
   525  func TestFS_Stream(t *testing.T) {
   526  	t.Parallel()
   527  	require := require.New(t)
   528  
   529  	// Start a server and client
   530  	s := nomad.TestServer(t, nil)
   531  	defer s.Shutdown()
   532  	testutil.WaitForLeader(t, s.RPC)
   533  
   534  	c := TestClient(t, func(c *config.Config) {
   535  		c.Servers = []string{s.GetConfig().RPCAddr.String()}
   536  	})
   537  	defer c.Shutdown()
   538  
   539  	// Force an allocation onto the node
   540  	expected := "Hello from the other side"
   541  	a := mock.Alloc()
   542  	a.Job.Type = structs.JobTypeBatch
   543  	a.NodeID = c.NodeID()
   544  	a.Job.TaskGroups[0].Count = 1
   545  	a.Job.TaskGroups[0].Tasks[0] = &structs.Task{
   546  		Name:   "web",
   547  		Driver: "mock_driver",
   548  		Config: map[string]interface{}{
   549  			"run_for":       "2s",
   550  			"stdout_string": expected,
   551  		},
   552  		LogConfig: structs.DefaultLogConfig(),
   553  		Resources: &structs.Resources{
   554  			CPU:      500,
   555  			MemoryMB: 256,
   556  		},
   557  	}
   558  
   559  	// Wait for the client to connect
   560  	testutil.WaitForResult(func() (bool, error) {
   561  		node, err := s.State().NodeByID(nil, c.NodeID())
   562  		if err != nil {
   563  			return false, err
   564  		}
   565  		if node == nil {
   566  			return false, fmt.Errorf("unknown node")
   567  		}
   568  
   569  		return node.Status == structs.NodeStatusReady, fmt.Errorf("bad node status")
   570  	}, func(err error) {
   571  		t.Fatal(err)
   572  	})
   573  
   574  	// Upsert the allocation
   575  	state := s.State()
   576  	require.Nil(state.UpsertJob(999, a.Job))
   577  	require.Nil(state.UpsertAllocs(1003, []*structs.Allocation{a}))
   578  
   579  	// Wait for the client to run the allocation
   580  	testutil.WaitForResult(func() (bool, error) {
   581  		alloc, err := state.AllocByID(nil, a.ID)
   582  		if err != nil {
   583  			return false, err
   584  		}
   585  		if alloc == nil {
   586  			return false, fmt.Errorf("unknown alloc")
   587  		}
   588  		if alloc.ClientStatus != structs.AllocClientStatusComplete {
   589  			return false, fmt.Errorf("alloc client status: %v", alloc.ClientStatus)
   590  		}
   591  
   592  		return true, nil
   593  	}, func(err error) {
   594  		t.Fatalf("Alloc on node %q not finished: %v", c.NodeID(), err)
   595  	})
   596  
   597  	// Make the request
   598  	req := &cstructs.FsStreamRequest{
   599  		AllocID:      a.ID,
   600  		Path:         "alloc/logs/web.stdout.0",
   601  		PlainText:    true,
   602  		QueryOptions: structs.QueryOptions{Region: "global"},
   603  	}
   604  
   605  	// Get the handler
   606  	handler, err := c.StreamingRpcHandler("FileSystem.Stream")
   607  	require.Nil(err)
   608  
   609  	// Create a pipe
   610  	p1, p2 := net.Pipe()
   611  	defer p1.Close()
   612  	defer p2.Close()
   613  
   614  	// Wrap the pipe so we can check it is closed
   615  	pipeChecker := &ReadWriteCloseChecker{ReadWriteCloser: p2}
   616  
   617  	errCh := make(chan error)
   618  	streamMsg := make(chan *cstructs.StreamErrWrapper)
   619  
   620  	// Start the handler
   621  	go handler(pipeChecker)
   622  
   623  	// Start the decoder
   624  	go func() {
   625  		decoder := codec.NewDecoder(p1, structs.MsgpackHandle)
   626  		for {
   627  			var msg cstructs.StreamErrWrapper
   628  			if err := decoder.Decode(&msg); err != nil {
   629  				if err == io.EOF || strings.Contains(err.Error(), "closed") {
   630  					return
   631  				}
   632  				errCh <- fmt.Errorf("error decoding: %v", err)
   633  			}
   634  
   635  			streamMsg <- &msg
   636  		}
   637  	}()
   638  
   639  	// Send the request
   640  	encoder := codec.NewEncoder(p1, structs.MsgpackHandle)
   641  	require.Nil(encoder.Encode(req))
   642  
   643  	timeout := time.After(3 * time.Second)
   644  	received := ""
   645  OUTER:
   646  	for {
   647  		select {
   648  		case <-timeout:
   649  			t.Fatal("timeout")
   650  		case err := <-errCh:
   651  			t.Fatal(err)
   652  		case msg := <-streamMsg:
   653  			if msg.Error != nil {
   654  				t.Fatalf("Got error: %v", msg.Error.Error())
   655  			}
   656  
   657  			// Add the payload
   658  			received += string(msg.Payload)
   659  			if received == expected {
   660  				break OUTER
   661  			}
   662  		}
   663  	}
   664  
   665  	testutil.WaitForResult(func() (bool, error) {
   666  		return pipeChecker.Closed, nil
   667  	}, func(err error) {
   668  		t.Fatal("Pipe not closed")
   669  	})
   670  }
   671  
   672  type ReadWriteCloseChecker struct {
   673  	io.ReadWriteCloser
   674  	Closed bool
   675  }
   676  
   677  func (r *ReadWriteCloseChecker) Close() error {
   678  	r.Closed = true
   679  	return r.ReadWriteCloser.Close()
   680  }
   681  
   682  func TestFS_Stream_Follow(t *testing.T) {
   683  	t.Parallel()
   684  	require := require.New(t)
   685  
   686  	// Start a server and client
   687  	s := nomad.TestServer(t, nil)
   688  	defer s.Shutdown()
   689  	testutil.WaitForLeader(t, s.RPC)
   690  
   691  	c := TestClient(t, func(c *config.Config) {
   692  		c.Servers = []string{s.GetConfig().RPCAddr.String()}
   693  	})
   694  	defer c.Shutdown()
   695  
   696  	// Force an allocation onto the node
   697  	expectedBase := "Hello from the other side"
   698  	repeat := 10
   699  
   700  	a := mock.Alloc()
   701  	a.Job.Type = structs.JobTypeBatch
   702  	a.NodeID = c.NodeID()
   703  	a.Job.TaskGroups[0].Count = 1
   704  	a.Job.TaskGroups[0].Tasks[0] = &structs.Task{
   705  		Name:   "web",
   706  		Driver: "mock_driver",
   707  		Config: map[string]interface{}{
   708  			"run_for":                "20s",
   709  			"stdout_string":          expectedBase,
   710  			"stdout_repeat":          repeat,
   711  			"stdout_repeat_duration": 200 * time.Millisecond,
   712  		},
   713  		LogConfig: structs.DefaultLogConfig(),
   714  		Resources: &structs.Resources{
   715  			CPU:      500,
   716  			MemoryMB: 256,
   717  		},
   718  	}
   719  
   720  	// Wait for the client to connect
   721  	testutil.WaitForResult(func() (bool, error) {
   722  		node, err := s.State().NodeByID(nil, c.NodeID())
   723  		if err != nil {
   724  			return false, err
   725  		}
   726  		if node == nil {
   727  			return false, fmt.Errorf("unknown node")
   728  		}
   729  
   730  		return node.Status == structs.NodeStatusReady, fmt.Errorf("bad node status")
   731  	}, func(err error) {
   732  		t.Fatal(err)
   733  	})
   734  
   735  	// Upsert the allocation
   736  	state := s.State()
   737  	require.Nil(state.UpsertJob(999, a.Job))
   738  	require.Nil(state.UpsertAllocs(1003, []*structs.Allocation{a}))
   739  
   740  	// Wait for the client to run the allocation
   741  	testutil.WaitForResult(func() (bool, error) {
   742  		alloc, err := state.AllocByID(nil, a.ID)
   743  		if err != nil {
   744  			return false, err
   745  		}
   746  		if alloc == nil {
   747  			return false, fmt.Errorf("unknown alloc")
   748  		}
   749  		if alloc.ClientStatus != structs.AllocClientStatusRunning {
   750  			return false, fmt.Errorf("alloc client status: %v", alloc.ClientStatus)
   751  		}
   752  
   753  		return true, nil
   754  	}, func(err error) {
   755  		t.Fatalf("Alloc on node %q not running: %v", c.NodeID(), err)
   756  	})
   757  
   758  	// Make the request
   759  	req := &cstructs.FsStreamRequest{
   760  		AllocID:      a.ID,
   761  		Path:         "alloc/logs/web.stdout.0",
   762  		PlainText:    true,
   763  		Follow:       true,
   764  		QueryOptions: structs.QueryOptions{Region: "global"},
   765  	}
   766  
   767  	// Get the handler
   768  	handler, err := c.StreamingRpcHandler("FileSystem.Stream")
   769  	require.Nil(err)
   770  
   771  	// Create a pipe
   772  	p1, p2 := net.Pipe()
   773  	defer p1.Close()
   774  	defer p2.Close()
   775  
   776  	errCh := make(chan error)
   777  	streamMsg := make(chan *cstructs.StreamErrWrapper)
   778  
   779  	// Start the handler
   780  	go handler(p2)
   781  
   782  	// Start the decoder
   783  	go func() {
   784  		decoder := codec.NewDecoder(p1, structs.MsgpackHandle)
   785  		for {
   786  			var msg cstructs.StreamErrWrapper
   787  			if err := decoder.Decode(&msg); err != nil {
   788  				if err == io.EOF || strings.Contains(err.Error(), "closed") {
   789  					return
   790  				}
   791  				errCh <- fmt.Errorf("error decoding: %v", err)
   792  			}
   793  
   794  			streamMsg <- &msg
   795  		}
   796  	}()
   797  
   798  	// Send the request
   799  	encoder := codec.NewEncoder(p1, structs.MsgpackHandle)
   800  	require.Nil(encoder.Encode(req))
   801  
   802  	timeout := time.After(20 * time.Second)
   803  	expected := strings.Repeat(expectedBase, repeat+1)
   804  	received := ""
   805  OUTER:
   806  	for {
   807  		select {
   808  		case <-timeout:
   809  			t.Fatal("timeout")
   810  		case err := <-errCh:
   811  			t.Fatal(err)
   812  		case msg := <-streamMsg:
   813  			if msg.Error != nil {
   814  				t.Fatalf("Got error: %v", msg.Error.Error())
   815  			}
   816  
   817  			// Add the payload
   818  			received += string(msg.Payload)
   819  			if received == expected {
   820  				break OUTER
   821  			}
   822  		}
   823  	}
   824  }
   825  
   826  func TestFS_Stream_Limit(t *testing.T) {
   827  	t.Parallel()
   828  	require := require.New(t)
   829  
   830  	// Start a server and client
   831  	s := nomad.TestServer(t, nil)
   832  	defer s.Shutdown()
   833  	testutil.WaitForLeader(t, s.RPC)
   834  
   835  	c := TestClient(t, func(c *config.Config) {
   836  		c.Servers = []string{s.GetConfig().RPCAddr.String()}
   837  	})
   838  	defer c.Shutdown()
   839  
   840  	// Force an allocation onto the node
   841  	var limit int64 = 5
   842  	full := "Hello from the other side"
   843  	expected := full[:limit]
   844  	a := mock.Alloc()
   845  	a.Job.Type = structs.JobTypeBatch
   846  	a.NodeID = c.NodeID()
   847  	a.Job.TaskGroups[0].Count = 1
   848  	a.Job.TaskGroups[0].Tasks[0] = &structs.Task{
   849  		Name:   "web",
   850  		Driver: "mock_driver",
   851  		Config: map[string]interface{}{
   852  			"run_for":       "2s",
   853  			"stdout_string": full,
   854  		},
   855  		LogConfig: structs.DefaultLogConfig(),
   856  		Resources: &structs.Resources{
   857  			CPU:      500,
   858  			MemoryMB: 256,
   859  		},
   860  	}
   861  
   862  	// Wait for the client to connect
   863  	testutil.WaitForResult(func() (bool, error) {
   864  		node, err := s.State().NodeByID(nil, c.NodeID())
   865  		if err != nil {
   866  			return false, err
   867  		}
   868  		if node == nil {
   869  			return false, fmt.Errorf("unknown node")
   870  		}
   871  
   872  		return node.Status == structs.NodeStatusReady, fmt.Errorf("bad node status")
   873  	}, func(err error) {
   874  		t.Fatal(err)
   875  	})
   876  
   877  	// Upsert the allocation
   878  	state := s.State()
   879  	require.Nil(state.UpsertJob(999, a.Job))
   880  	require.Nil(state.UpsertAllocs(1003, []*structs.Allocation{a}))
   881  
   882  	// Wait for the client to run the allocation
   883  	testutil.WaitForResult(func() (bool, error) {
   884  		alloc, err := state.AllocByID(nil, a.ID)
   885  		if err != nil {
   886  			return false, err
   887  		}
   888  		if alloc == nil {
   889  			return false, fmt.Errorf("unknown alloc")
   890  		}
   891  		if alloc.ClientStatus != structs.AllocClientStatusComplete {
   892  			return false, fmt.Errorf("alloc client status: %v", alloc.ClientStatus)
   893  		}
   894  
   895  		return true, nil
   896  	}, func(err error) {
   897  		t.Fatalf("Alloc on node %q not finished: %v", c.NodeID(), err)
   898  	})
   899  
   900  	// Make the request
   901  	req := &cstructs.FsStreamRequest{
   902  		AllocID:      a.ID,
   903  		Path:         "alloc/logs/web.stdout.0",
   904  		PlainText:    true,
   905  		Limit:        limit,
   906  		QueryOptions: structs.QueryOptions{Region: "global"},
   907  	}
   908  
   909  	// Get the handler
   910  	handler, err := c.StreamingRpcHandler("FileSystem.Stream")
   911  	require.Nil(err)
   912  
   913  	// Create a pipe
   914  	p1, p2 := net.Pipe()
   915  	defer p1.Close()
   916  	defer p2.Close()
   917  
   918  	errCh := make(chan error)
   919  	streamMsg := make(chan *cstructs.StreamErrWrapper)
   920  
   921  	// Start the handler
   922  	go handler(p2)
   923  
   924  	// Start the decoder
   925  	go func() {
   926  		decoder := codec.NewDecoder(p1, structs.MsgpackHandle)
   927  		for {
   928  			var msg cstructs.StreamErrWrapper
   929  			if err := decoder.Decode(&msg); err != nil {
   930  				if err == io.EOF || strings.Contains(err.Error(), "closed") {
   931  					return
   932  				}
   933  				errCh <- fmt.Errorf("error decoding: %v", err)
   934  			}
   935  
   936  			streamMsg <- &msg
   937  		}
   938  	}()
   939  
   940  	// Send the request
   941  	encoder := codec.NewEncoder(p1, structs.MsgpackHandle)
   942  	require.Nil(encoder.Encode(req))
   943  
   944  	timeout := time.After(3 * time.Second)
   945  	received := ""
   946  OUTER:
   947  	for {
   948  		select {
   949  		case <-timeout:
   950  			t.Fatal("timeout")
   951  		case err := <-errCh:
   952  			t.Fatal(err)
   953  		case msg := <-streamMsg:
   954  			if msg.Error != nil {
   955  				t.Fatalf("Got error: %v", msg.Error.Error())
   956  			}
   957  
   958  			// Add the payload
   959  			received += string(msg.Payload)
   960  			if received == expected {
   961  				break OUTER
   962  			}
   963  		}
   964  	}
   965  }
   966  
   967  func TestFS_Logs_NoAlloc(t *testing.T) {
   968  	t.Parallel()
   969  	require := require.New(t)
   970  
   971  	// Start a client
   972  	c := TestClient(t, nil)
   973  	defer c.Shutdown()
   974  
   975  	// Make the request with bad allocation id
   976  	req := &cstructs.FsLogsRequest{
   977  		AllocID:      uuid.Generate(),
   978  		Task:         "foo",
   979  		LogType:      "stdout",
   980  		Origin:       "start",
   981  		QueryOptions: structs.QueryOptions{Region: "global"},
   982  	}
   983  
   984  	// Get the handler
   985  	handler, err := c.StreamingRpcHandler("FileSystem.Logs")
   986  	require.Nil(err)
   987  
   988  	// Create a pipe
   989  	p1, p2 := net.Pipe()
   990  	defer p1.Close()
   991  	defer p2.Close()
   992  
   993  	errCh := make(chan error)
   994  	streamMsg := make(chan *cstructs.StreamErrWrapper)
   995  
   996  	// Start the handler
   997  	go handler(p2)
   998  
   999  	// Start the decoder
  1000  	go func() {
  1001  		decoder := codec.NewDecoder(p1, structs.MsgpackHandle)
  1002  		for {
  1003  			var msg cstructs.StreamErrWrapper
  1004  			if err := decoder.Decode(&msg); err != nil {
  1005  				if err == io.EOF || strings.Contains(err.Error(), "closed") {
  1006  					return
  1007  				}
  1008  				errCh <- fmt.Errorf("error decoding: %v", err)
  1009  			}
  1010  
  1011  			streamMsg <- &msg
  1012  		}
  1013  	}()
  1014  
  1015  	// Send the request
  1016  	encoder := codec.NewEncoder(p1, structs.MsgpackHandle)
  1017  	require.Nil(encoder.Encode(req))
  1018  
  1019  	timeout := time.After(3 * time.Second)
  1020  
  1021  OUTER:
  1022  	for {
  1023  		select {
  1024  		case <-timeout:
  1025  			t.Fatal("timeout")
  1026  		case err := <-errCh:
  1027  			t.Fatal(err)
  1028  		case msg := <-streamMsg:
  1029  			t.Logf("Got msg %+v", msg)
  1030  			if msg.Error == nil {
  1031  				continue
  1032  			}
  1033  
  1034  			if structs.IsErrUnknownAllocation(msg.Error) {
  1035  				break OUTER
  1036  			} else {
  1037  				t.Fatalf("bad error: %v", err)
  1038  			}
  1039  		}
  1040  	}
  1041  }
  1042  
  1043  func TestFS_Logs_ACL(t *testing.T) {
  1044  	t.Parallel()
  1045  	require := require.New(t)
  1046  
  1047  	// Start a server
  1048  	s, root := nomad.TestACLServer(t, nil)
  1049  	defer s.Shutdown()
  1050  	testutil.WaitForLeader(t, s.RPC)
  1051  
  1052  	client := TestClient(t, func(c *config.Config) {
  1053  		c.ACLEnabled = true
  1054  		c.Servers = []string{s.GetConfig().RPCAddr.String()}
  1055  	})
  1056  	defer client.Shutdown()
  1057  
  1058  	// Create a bad token
  1059  	policyBad := mock.NamespacePolicy("other", "", []string{acl.NamespaceCapabilityReadFS})
  1060  	tokenBad := mock.CreatePolicyAndToken(t, s.State(), 1005, "invalid", policyBad)
  1061  
  1062  	policyGood := mock.NamespacePolicy(structs.DefaultNamespace, "",
  1063  		[]string{acl.NamespaceCapabilityReadLogs, acl.NamespaceCapabilityReadFS})
  1064  	tokenGood := mock.CreatePolicyAndToken(t, s.State(), 1009, "valid2", policyGood)
  1065  
  1066  	cases := []struct {
  1067  		Name          string
  1068  		Token         string
  1069  		ExpectedError string
  1070  	}{
  1071  		{
  1072  			Name:          "bad token",
  1073  			Token:         tokenBad.SecretID,
  1074  			ExpectedError: structs.ErrPermissionDenied.Error(),
  1075  		},
  1076  		{
  1077  			Name:          "good token",
  1078  			Token:         tokenGood.SecretID,
  1079  			ExpectedError: structs.ErrUnknownAllocationPrefix,
  1080  		},
  1081  		{
  1082  			Name:          "root token",
  1083  			Token:         root.SecretID,
  1084  			ExpectedError: structs.ErrUnknownAllocationPrefix,
  1085  		},
  1086  	}
  1087  
  1088  	for _, c := range cases {
  1089  		t.Run(c.Name, func(t *testing.T) {
  1090  			// Make the request with bad allocation id
  1091  			req := &cstructs.FsLogsRequest{
  1092  				AllocID: uuid.Generate(),
  1093  				Task:    "foo",
  1094  				LogType: "stdout",
  1095  				Origin:  "start",
  1096  				QueryOptions: structs.QueryOptions{
  1097  					Namespace: structs.DefaultNamespace,
  1098  					Region:    "global",
  1099  					AuthToken: c.Token,
  1100  				},
  1101  			}
  1102  
  1103  			// Get the handler
  1104  			handler, err := client.StreamingRpcHandler("FileSystem.Logs")
  1105  			require.Nil(err)
  1106  
  1107  			// Create a pipe
  1108  			p1, p2 := net.Pipe()
  1109  			defer p1.Close()
  1110  			defer p2.Close()
  1111  
  1112  			errCh := make(chan error)
  1113  			streamMsg := make(chan *cstructs.StreamErrWrapper)
  1114  
  1115  			// Start the handler
  1116  			go handler(p2)
  1117  
  1118  			// Start the decoder
  1119  			go func() {
  1120  				decoder := codec.NewDecoder(p1, structs.MsgpackHandle)
  1121  				for {
  1122  					var msg cstructs.StreamErrWrapper
  1123  					if err := decoder.Decode(&msg); err != nil {
  1124  						if err == io.EOF || strings.Contains(err.Error(), "closed") {
  1125  							return
  1126  						}
  1127  						errCh <- fmt.Errorf("error decoding: %v", err)
  1128  					}
  1129  
  1130  					streamMsg <- &msg
  1131  				}
  1132  			}()
  1133  
  1134  			// Send the request
  1135  			encoder := codec.NewEncoder(p1, structs.MsgpackHandle)
  1136  			require.Nil(encoder.Encode(req))
  1137  
  1138  			timeout := time.After(5 * time.Second)
  1139  
  1140  		OUTER:
  1141  			for {
  1142  				select {
  1143  				case <-timeout:
  1144  					t.Fatal("timeout")
  1145  				case err := <-errCh:
  1146  					t.Fatal(err)
  1147  				case msg := <-streamMsg:
  1148  					if msg.Error == nil {
  1149  						continue
  1150  					}
  1151  
  1152  					if strings.Contains(msg.Error.Error(), c.ExpectedError) {
  1153  						break OUTER
  1154  					} else {
  1155  						t.Fatalf("Bad error: %v", msg.Error)
  1156  					}
  1157  				}
  1158  			}
  1159  		})
  1160  	}
  1161  }
  1162  
  1163  func TestFS_Logs(t *testing.T) {
  1164  	t.Parallel()
  1165  	require := require.New(t)
  1166  
  1167  	// Start a server and client
  1168  	s := nomad.TestServer(t, nil)
  1169  	defer s.Shutdown()
  1170  	testutil.WaitForLeader(t, s.RPC)
  1171  
  1172  	c := TestClient(t, func(c *config.Config) {
  1173  		c.Servers = []string{s.GetConfig().RPCAddr.String()}
  1174  	})
  1175  	defer c.Shutdown()
  1176  
  1177  	// Force an allocation onto the node
  1178  	expected := "Hello from the other side"
  1179  	a := mock.Alloc()
  1180  	a.Job.Type = structs.JobTypeBatch
  1181  	a.NodeID = c.NodeID()
  1182  	a.Job.TaskGroups[0].Count = 1
  1183  	a.Job.TaskGroups[0].Tasks[0] = &structs.Task{
  1184  		Name:   "web",
  1185  		Driver: "mock_driver",
  1186  		Config: map[string]interface{}{
  1187  			"run_for":       "2s",
  1188  			"stdout_string": expected,
  1189  		},
  1190  		LogConfig: structs.DefaultLogConfig(),
  1191  		Resources: &structs.Resources{
  1192  			CPU:      500,
  1193  			MemoryMB: 256,
  1194  		},
  1195  	}
  1196  
  1197  	// Wait for the client to connect
  1198  	testutil.WaitForResult(func() (bool, error) {
  1199  		node, err := s.State().NodeByID(nil, c.NodeID())
  1200  		if err != nil {
  1201  			return false, err
  1202  		}
  1203  		if node == nil {
  1204  			return false, fmt.Errorf("unknown node")
  1205  		}
  1206  
  1207  		return node.Status == structs.NodeStatusReady, fmt.Errorf("bad node status")
  1208  	}, func(err error) {
  1209  		t.Fatal(err)
  1210  	})
  1211  
  1212  	// Upsert the allocation
  1213  	state := s.State()
  1214  	require.Nil(state.UpsertJob(999, a.Job))
  1215  	require.Nil(state.UpsertAllocs(1003, []*structs.Allocation{a}))
  1216  
  1217  	// Wait for the client to run the allocation
  1218  	testutil.WaitForResult(func() (bool, error) {
  1219  		alloc, err := state.AllocByID(nil, a.ID)
  1220  		if err != nil {
  1221  			return false, err
  1222  		}
  1223  		if alloc == nil {
  1224  			return false, fmt.Errorf("unknown alloc")
  1225  		}
  1226  		if alloc.ClientStatus != structs.AllocClientStatusComplete {
  1227  			return false, fmt.Errorf("alloc client status: %v", alloc.ClientStatus)
  1228  		}
  1229  
  1230  		return true, nil
  1231  	}, func(err error) {
  1232  		t.Fatalf("Alloc on node %q not finished: %v", c.NodeID(), err)
  1233  	})
  1234  
  1235  	// Make the request
  1236  	req := &cstructs.FsLogsRequest{
  1237  		AllocID:      a.ID,
  1238  		Task:         a.Job.TaskGroups[0].Tasks[0].Name,
  1239  		LogType:      "stdout",
  1240  		Origin:       "start",
  1241  		PlainText:    true,
  1242  		QueryOptions: structs.QueryOptions{Region: "global"},
  1243  	}
  1244  
  1245  	// Get the handler
  1246  	handler, err := c.StreamingRpcHandler("FileSystem.Logs")
  1247  	require.Nil(err)
  1248  
  1249  	// Create a pipe
  1250  	p1, p2 := net.Pipe()
  1251  	defer p1.Close()
  1252  	defer p2.Close()
  1253  
  1254  	errCh := make(chan error)
  1255  	streamMsg := make(chan *cstructs.StreamErrWrapper)
  1256  
  1257  	// Start the handler
  1258  	go handler(p2)
  1259  
  1260  	// Start the decoder
  1261  	go func() {
  1262  		decoder := codec.NewDecoder(p1, structs.MsgpackHandle)
  1263  		for {
  1264  			var msg cstructs.StreamErrWrapper
  1265  			if err := decoder.Decode(&msg); err != nil {
  1266  				if err == io.EOF || strings.Contains(err.Error(), "closed") {
  1267  					return
  1268  				}
  1269  				errCh <- fmt.Errorf("error decoding: %v", err)
  1270  			}
  1271  
  1272  			streamMsg <- &msg
  1273  		}
  1274  	}()
  1275  
  1276  	// Send the request
  1277  	encoder := codec.NewEncoder(p1, structs.MsgpackHandle)
  1278  	require.Nil(encoder.Encode(req))
  1279  
  1280  	timeout := time.After(3 * time.Second)
  1281  	received := ""
  1282  OUTER:
  1283  	for {
  1284  		select {
  1285  		case <-timeout:
  1286  			t.Fatal("timeout")
  1287  		case err := <-errCh:
  1288  			t.Fatal(err)
  1289  		case msg := <-streamMsg:
  1290  			if msg.Error != nil {
  1291  				t.Fatalf("Got error: %v", msg.Error.Error())
  1292  			}
  1293  
  1294  			// Add the payload
  1295  			received += string(msg.Payload)
  1296  			if received == expected {
  1297  				break OUTER
  1298  			}
  1299  		}
  1300  	}
  1301  }
  1302  
  1303  func TestFS_Logs_Follow(t *testing.T) {
  1304  	t.Parallel()
  1305  	require := require.New(t)
  1306  
  1307  	// Start a server and client
  1308  	s := nomad.TestServer(t, nil)
  1309  	defer s.Shutdown()
  1310  	testutil.WaitForLeader(t, s.RPC)
  1311  
  1312  	c := TestClient(t, func(c *config.Config) {
  1313  		c.Servers = []string{s.GetConfig().RPCAddr.String()}
  1314  	})
  1315  	defer c.Shutdown()
  1316  
  1317  	// Force an allocation onto the node
  1318  	expectedBase := "Hello from the other side"
  1319  	repeat := 10
  1320  
  1321  	a := mock.Alloc()
  1322  	a.Job.Type = structs.JobTypeBatch
  1323  	a.NodeID = c.NodeID()
  1324  	a.Job.TaskGroups[0].Count = 1
  1325  	a.Job.TaskGroups[0].Tasks[0] = &structs.Task{
  1326  		Name:   "web",
  1327  		Driver: "mock_driver",
  1328  		Config: map[string]interface{}{
  1329  			"run_for":                "20s",
  1330  			"stdout_string":          expectedBase,
  1331  			"stdout_repeat":          repeat,
  1332  			"stdout_repeat_duration": 200 * time.Millisecond,
  1333  		},
  1334  		LogConfig: structs.DefaultLogConfig(),
  1335  		Resources: &structs.Resources{
  1336  			CPU:      500,
  1337  			MemoryMB: 256,
  1338  		},
  1339  	}
  1340  
  1341  	// Wait for the client to connect
  1342  	testutil.WaitForResult(func() (bool, error) {
  1343  		node, err := s.State().NodeByID(nil, c.NodeID())
  1344  		if err != nil {
  1345  			return false, err
  1346  		}
  1347  		if node == nil {
  1348  			return false, fmt.Errorf("unknown node")
  1349  		}
  1350  
  1351  		return node.Status == structs.NodeStatusReady, fmt.Errorf("bad node status")
  1352  	}, func(err error) {
  1353  		t.Fatal(err)
  1354  	})
  1355  
  1356  	// Upsert the allocation
  1357  	state := s.State()
  1358  	require.Nil(state.UpsertJob(999, a.Job))
  1359  	require.Nil(state.UpsertAllocs(1003, []*structs.Allocation{a}))
  1360  
  1361  	// Wait for the client to run the allocation
  1362  	testutil.WaitForResult(func() (bool, error) {
  1363  		alloc, err := state.AllocByID(nil, a.ID)
  1364  		if err != nil {
  1365  			return false, err
  1366  		}
  1367  		if alloc == nil {
  1368  			return false, fmt.Errorf("unknown alloc")
  1369  		}
  1370  		if alloc.ClientStatus != structs.AllocClientStatusRunning {
  1371  			return false, fmt.Errorf("alloc client status: %v", alloc.ClientStatus)
  1372  		}
  1373  
  1374  		return true, nil
  1375  	}, func(err error) {
  1376  		t.Fatalf("Alloc on node %q not running: %v", c.NodeID(), err)
  1377  	})
  1378  
  1379  	// Make the request
  1380  	req := &cstructs.FsLogsRequest{
  1381  		AllocID:      a.ID,
  1382  		Task:         a.Job.TaskGroups[0].Tasks[0].Name,
  1383  		LogType:      "stdout",
  1384  		Origin:       "start",
  1385  		PlainText:    true,
  1386  		Follow:       true,
  1387  		QueryOptions: structs.QueryOptions{Region: "global"},
  1388  	}
  1389  
  1390  	// Get the handler
  1391  	handler, err := c.StreamingRpcHandler("FileSystem.Logs")
  1392  	require.Nil(err)
  1393  
  1394  	// Create a pipe
  1395  	p1, p2 := net.Pipe()
  1396  	defer p1.Close()
  1397  	defer p2.Close()
  1398  
  1399  	errCh := make(chan error)
  1400  	streamMsg := make(chan *cstructs.StreamErrWrapper)
  1401  
  1402  	// Start the handler
  1403  	go handler(p2)
  1404  
  1405  	// Start the decoder
  1406  	go func() {
  1407  		decoder := codec.NewDecoder(p1, structs.MsgpackHandle)
  1408  		for {
  1409  			var msg cstructs.StreamErrWrapper
  1410  			if err := decoder.Decode(&msg); err != nil {
  1411  				if err == io.EOF || strings.Contains(err.Error(), "closed") {
  1412  					return
  1413  				}
  1414  				errCh <- fmt.Errorf("error decoding: %v", err)
  1415  			}
  1416  
  1417  			streamMsg <- &msg
  1418  		}
  1419  	}()
  1420  
  1421  	// Send the request
  1422  	encoder := codec.NewEncoder(p1, structs.MsgpackHandle)
  1423  	require.Nil(encoder.Encode(req))
  1424  
  1425  	timeout := time.After(20 * time.Second)
  1426  	expected := strings.Repeat(expectedBase, repeat+1)
  1427  	received := ""
  1428  OUTER:
  1429  	for {
  1430  		select {
  1431  		case <-timeout:
  1432  			t.Fatal("timeout")
  1433  		case err := <-errCh:
  1434  			t.Fatal(err)
  1435  		case msg := <-streamMsg:
  1436  			if msg.Error != nil {
  1437  				t.Fatalf("Got error: %v", msg.Error.Error())
  1438  			}
  1439  
  1440  			// Add the payload
  1441  			received += string(msg.Payload)
  1442  			if received == expected {
  1443  				break OUTER
  1444  			}
  1445  		}
  1446  	}
  1447  }
  1448  
  1449  func TestFS_findClosest(t *testing.T) {
  1450  	task := "foo"
  1451  	entries := []*cstructs.AllocFileInfo{
  1452  		{
  1453  			Name: "foo.stdout.0",
  1454  			Size: 100,
  1455  		},
  1456  		{
  1457  			Name: "foo.stdout.1",
  1458  			Size: 100,
  1459  		},
  1460  		{
  1461  			Name: "foo.stdout.2",
  1462  			Size: 100,
  1463  		},
  1464  		{
  1465  			Name: "foo.stdout.3",
  1466  			Size: 100,
  1467  		},
  1468  		{
  1469  			Name: "foo.stderr.0",
  1470  			Size: 100,
  1471  		},
  1472  		{
  1473  			Name: "foo.stderr.1",
  1474  			Size: 100,
  1475  		},
  1476  		{
  1477  			Name: "foo.stderr.2",
  1478  			Size: 100,
  1479  		},
  1480  	}
  1481  
  1482  	cases := []struct {
  1483  		Entries        []*cstructs.AllocFileInfo
  1484  		DesiredIdx     int64
  1485  		DesiredOffset  int64
  1486  		Task           string
  1487  		LogType        string
  1488  		ExpectedFile   string
  1489  		ExpectedIdx    int64
  1490  		ExpectedOffset int64
  1491  		Error          bool
  1492  	}{
  1493  		// Test error cases
  1494  		{
  1495  			Entries:    nil,
  1496  			DesiredIdx: 0,
  1497  			Task:       task,
  1498  			LogType:    "stdout",
  1499  			Error:      true,
  1500  		},
  1501  		{
  1502  			Entries:    entries[0:3],
  1503  			DesiredIdx: 0,
  1504  			Task:       task,
  1505  			LogType:    "stderr",
  1506  			Error:      true,
  1507  		},
  1508  
  1509  		// Test beginning cases
  1510  		{
  1511  			Entries:      entries,
  1512  			DesiredIdx:   0,
  1513  			Task:         task,
  1514  			LogType:      "stdout",
  1515  			ExpectedFile: entries[0].Name,
  1516  			ExpectedIdx:  0,
  1517  		},
  1518  		{
  1519  			// Desired offset should be ignored at edges
  1520  			Entries:        entries,
  1521  			DesiredIdx:     0,
  1522  			DesiredOffset:  -100,
  1523  			Task:           task,
  1524  			LogType:        "stdout",
  1525  			ExpectedFile:   entries[0].Name,
  1526  			ExpectedIdx:    0,
  1527  			ExpectedOffset: 0,
  1528  		},
  1529  		{
  1530  			// Desired offset should be ignored at edges
  1531  			Entries:        entries,
  1532  			DesiredIdx:     1,
  1533  			DesiredOffset:  -1000,
  1534  			Task:           task,
  1535  			LogType:        "stdout",
  1536  			ExpectedFile:   entries[0].Name,
  1537  			ExpectedIdx:    0,
  1538  			ExpectedOffset: 0,
  1539  		},
  1540  		{
  1541  			Entries:      entries,
  1542  			DesiredIdx:   0,
  1543  			Task:         task,
  1544  			LogType:      "stderr",
  1545  			ExpectedFile: entries[4].Name,
  1546  			ExpectedIdx:  0,
  1547  		},
  1548  		{
  1549  			Entries:      entries,
  1550  			DesiredIdx:   0,
  1551  			Task:         task,
  1552  			LogType:      "stdout",
  1553  			ExpectedFile: entries[0].Name,
  1554  			ExpectedIdx:  0,
  1555  		},
  1556  
  1557  		// Test middle cases
  1558  		{
  1559  			Entries:      entries,
  1560  			DesiredIdx:   1,
  1561  			Task:         task,
  1562  			LogType:      "stdout",
  1563  			ExpectedFile: entries[1].Name,
  1564  			ExpectedIdx:  1,
  1565  		},
  1566  		{
  1567  			Entries:        entries,
  1568  			DesiredIdx:     1,
  1569  			DesiredOffset:  10,
  1570  			Task:           task,
  1571  			LogType:        "stdout",
  1572  			ExpectedFile:   entries[1].Name,
  1573  			ExpectedIdx:    1,
  1574  			ExpectedOffset: 10,
  1575  		},
  1576  		{
  1577  			Entries:        entries,
  1578  			DesiredIdx:     1,
  1579  			DesiredOffset:  110,
  1580  			Task:           task,
  1581  			LogType:        "stdout",
  1582  			ExpectedFile:   entries[2].Name,
  1583  			ExpectedIdx:    2,
  1584  			ExpectedOffset: 10,
  1585  		},
  1586  		{
  1587  			Entries:      entries,
  1588  			DesiredIdx:   1,
  1589  			Task:         task,
  1590  			LogType:      "stderr",
  1591  			ExpectedFile: entries[5].Name,
  1592  			ExpectedIdx:  1,
  1593  		},
  1594  		// Test end cases
  1595  		{
  1596  			Entries:      entries,
  1597  			DesiredIdx:   math.MaxInt64,
  1598  			Task:         task,
  1599  			LogType:      "stdout",
  1600  			ExpectedFile: entries[3].Name,
  1601  			ExpectedIdx:  3,
  1602  		},
  1603  		{
  1604  			Entries:        entries,
  1605  			DesiredIdx:     math.MaxInt64,
  1606  			DesiredOffset:  math.MaxInt64,
  1607  			Task:           task,
  1608  			LogType:        "stdout",
  1609  			ExpectedFile:   entries[3].Name,
  1610  			ExpectedIdx:    3,
  1611  			ExpectedOffset: 100,
  1612  		},
  1613  		{
  1614  			Entries:        entries,
  1615  			DesiredIdx:     math.MaxInt64,
  1616  			DesiredOffset:  -10,
  1617  			Task:           task,
  1618  			LogType:        "stdout",
  1619  			ExpectedFile:   entries[3].Name,
  1620  			ExpectedIdx:    3,
  1621  			ExpectedOffset: 90,
  1622  		},
  1623  		{
  1624  			Entries:      entries,
  1625  			DesiredIdx:   math.MaxInt64,
  1626  			Task:         task,
  1627  			LogType:      "stderr",
  1628  			ExpectedFile: entries[6].Name,
  1629  			ExpectedIdx:  2,
  1630  		},
  1631  	}
  1632  
  1633  	for i, c := range cases {
  1634  		entry, idx, offset, err := findClosest(c.Entries, c.DesiredIdx, c.DesiredOffset, c.Task, c.LogType)
  1635  		if err != nil {
  1636  			if !c.Error {
  1637  				t.Fatalf("case %d: Unexpected error: %v", i, err)
  1638  			}
  1639  			continue
  1640  		}
  1641  
  1642  		if entry.Name != c.ExpectedFile {
  1643  			t.Fatalf("case %d: Got file %q; want %q", i, entry.Name, c.ExpectedFile)
  1644  		}
  1645  		if idx != c.ExpectedIdx {
  1646  			t.Fatalf("case %d: Got index %d; want %d", i, idx, c.ExpectedIdx)
  1647  		}
  1648  		if offset != c.ExpectedOffset {
  1649  			t.Fatalf("case %d: Got offset %d; want %d", i, offset, c.ExpectedOffset)
  1650  		}
  1651  	}
  1652  }
  1653  
  1654  func TestFS_streamFile_NoFile(t *testing.T) {
  1655  	t.Parallel()
  1656  	require := require.New(t)
  1657  	c := TestClient(t, nil)
  1658  	defer c.Shutdown()
  1659  
  1660  	ad := tempAllocDir(t)
  1661  	defer os.RemoveAll(ad.AllocDir)
  1662  
  1663  	frames := make(chan *sframer.StreamFrame, 32)
  1664  	framer := sframer.NewStreamFramer(frames, streamHeartbeatRate, streamBatchWindow, streamFrameSize)
  1665  	framer.Run()
  1666  	defer framer.Destroy()
  1667  
  1668  	err := c.endpoints.FileSystem.streamFile(
  1669  		context.Background(), 0, "foo", 0, ad, framer, nil)
  1670  	require.NotNil(err)
  1671  	require.Contains(err.Error(), "no such file")
  1672  }
  1673  
  1674  func TestFS_streamFile_Modify(t *testing.T) {
  1675  	t.Parallel()
  1676  
  1677  	c := TestClient(t, nil)
  1678  	defer c.Shutdown()
  1679  
  1680  	// Get a temp alloc dir
  1681  	ad := tempAllocDir(t)
  1682  	defer os.RemoveAll(ad.AllocDir)
  1683  
  1684  	// Create a file in the temp dir
  1685  	streamFile := "stream_file"
  1686  	f, err := os.Create(filepath.Join(ad.AllocDir, streamFile))
  1687  	if err != nil {
  1688  		t.Fatalf("Failed to create file: %v", err)
  1689  	}
  1690  	defer f.Close()
  1691  
  1692  	data := []byte("helloworld")
  1693  
  1694  	// Start the reader
  1695  	resultCh := make(chan struct{})
  1696  	frames := make(chan *sframer.StreamFrame, 4)
  1697  	go func() {
  1698  		var collected []byte
  1699  		for {
  1700  			frame := <-frames
  1701  			if frame.IsHeartbeat() {
  1702  				continue
  1703  			}
  1704  
  1705  			collected = append(collected, frame.Data...)
  1706  			if reflect.DeepEqual(data, collected) {
  1707  				resultCh <- struct{}{}
  1708  				return
  1709  			}
  1710  		}
  1711  	}()
  1712  
  1713  	// Write a few bytes
  1714  	if _, err := f.Write(data[:3]); err != nil {
  1715  		t.Fatalf("write failed: %v", err)
  1716  	}
  1717  
  1718  	framer := sframer.NewStreamFramer(frames, streamHeartbeatRate, streamBatchWindow, streamFrameSize)
  1719  	framer.Run()
  1720  	defer framer.Destroy()
  1721  
  1722  	// Start streaming
  1723  	go func() {
  1724  		if err := c.endpoints.FileSystem.streamFile(
  1725  			context.Background(), 0, streamFile, 0, ad, framer, nil); err != nil {
  1726  			t.Fatalf("stream() failed: %v", err)
  1727  		}
  1728  	}()
  1729  
  1730  	// Sleep a little before writing more. This lets us check if the watch
  1731  	// is working.
  1732  	time.Sleep(1 * time.Duration(testutil.TestMultiplier()) * time.Second)
  1733  	if _, err := f.Write(data[3:]); err != nil {
  1734  		t.Fatalf("write failed: %v", err)
  1735  	}
  1736  
  1737  	select {
  1738  	case <-resultCh:
  1739  	case <-time.After(10 * time.Duration(testutil.TestMultiplier()) * streamBatchWindow):
  1740  		t.Fatalf("failed to send new data")
  1741  	}
  1742  }
  1743  
  1744  func TestFS_streamFile_Truncate(t *testing.T) {
  1745  	t.Parallel()
  1746  	c := TestClient(t, nil)
  1747  	defer c.Shutdown()
  1748  
  1749  	// Get a temp alloc dir
  1750  	ad := tempAllocDir(t)
  1751  	defer os.RemoveAll(ad.AllocDir)
  1752  
  1753  	// Create a file in the temp dir
  1754  	data := []byte("helloworld")
  1755  	streamFile := "stream_file"
  1756  	streamFilePath := filepath.Join(ad.AllocDir, streamFile)
  1757  	f, err := os.Create(streamFilePath)
  1758  	if err != nil {
  1759  		t.Fatalf("Failed to create file: %v", err)
  1760  	}
  1761  	defer f.Close()
  1762  
  1763  	// Start the reader
  1764  	truncateCh := make(chan struct{})
  1765  	truncateClosed := false
  1766  	dataPostTruncCh := make(chan struct{})
  1767  	frames := make(chan *sframer.StreamFrame, 4)
  1768  	go func() {
  1769  		var collected []byte
  1770  		for {
  1771  			frame := <-frames
  1772  			if frame.IsHeartbeat() {
  1773  				continue
  1774  			}
  1775  
  1776  			if frame.FileEvent == truncateEvent && !truncateClosed {
  1777  				close(truncateCh)
  1778  				truncateClosed = true
  1779  			}
  1780  
  1781  			collected = append(collected, frame.Data...)
  1782  			if reflect.DeepEqual(data, collected) {
  1783  				close(dataPostTruncCh)
  1784  				return
  1785  			}
  1786  		}
  1787  	}()
  1788  
  1789  	// Write a few bytes
  1790  	if _, err := f.Write(data[:3]); err != nil {
  1791  		t.Fatalf("write failed: %v", err)
  1792  	}
  1793  
  1794  	framer := sframer.NewStreamFramer(frames, streamHeartbeatRate, streamBatchWindow, streamFrameSize)
  1795  	framer.Run()
  1796  	defer framer.Destroy()
  1797  
  1798  	// Start streaming
  1799  	go func() {
  1800  		if err := c.endpoints.FileSystem.streamFile(
  1801  			context.Background(), 0, streamFile, 0, ad, framer, nil); err != nil {
  1802  			t.Fatalf("stream() failed: %v", err)
  1803  		}
  1804  	}()
  1805  
  1806  	// Sleep a little before truncating. This lets us check if the watch
  1807  	// is working.
  1808  	time.Sleep(1 * time.Duration(testutil.TestMultiplier()) * time.Second)
  1809  	if err := f.Truncate(0); err != nil {
  1810  		t.Fatalf("truncate failed: %v", err)
  1811  	}
  1812  	if err := f.Sync(); err != nil {
  1813  		t.Fatalf("sync failed: %v", err)
  1814  	}
  1815  	if err := f.Close(); err != nil {
  1816  		t.Fatalf("failed to close file: %v", err)
  1817  	}
  1818  
  1819  	f2, err := os.OpenFile(streamFilePath, os.O_RDWR, 0)
  1820  	if err != nil {
  1821  		t.Fatalf("failed to reopen file: %v", err)
  1822  	}
  1823  	defer f2.Close()
  1824  	if _, err := f2.Write(data[3:5]); err != nil {
  1825  		t.Fatalf("write failed: %v", err)
  1826  	}
  1827  
  1828  	select {
  1829  	case <-truncateCh:
  1830  	case <-time.After(10 * time.Duration(testutil.TestMultiplier()) * streamBatchWindow):
  1831  		t.Fatalf("did not receive truncate")
  1832  	}
  1833  
  1834  	// Sleep a little before writing more. This lets us check if the watch
  1835  	// is working.
  1836  	time.Sleep(1 * time.Duration(testutil.TestMultiplier()) * time.Second)
  1837  	if _, err := f2.Write(data[5:]); err != nil {
  1838  		t.Fatalf("write failed: %v", err)
  1839  	}
  1840  
  1841  	select {
  1842  	case <-dataPostTruncCh:
  1843  	case <-time.After(10 * time.Duration(testutil.TestMultiplier()) * streamBatchWindow):
  1844  		t.Fatalf("did not receive post truncate data")
  1845  	}
  1846  }
  1847  
  1848  func TestFS_streamImpl_Delete(t *testing.T) {
  1849  	t.Parallel()
  1850  
  1851  	c := TestClient(t, nil)
  1852  	defer c.Shutdown()
  1853  
  1854  	// Get a temp alloc dir
  1855  	ad := tempAllocDir(t)
  1856  	defer os.RemoveAll(ad.AllocDir)
  1857  
  1858  	// Create a file in the temp dir
  1859  	data := []byte("helloworld")
  1860  	streamFile := "stream_file"
  1861  	streamFilePath := filepath.Join(ad.AllocDir, streamFile)
  1862  	f, err := os.Create(streamFilePath)
  1863  	if err != nil {
  1864  		t.Fatalf("Failed to create file: %v", err)
  1865  	}
  1866  	defer f.Close()
  1867  
  1868  	// Start the reader
  1869  	deleteCh := make(chan struct{})
  1870  	frames := make(chan *sframer.StreamFrame, 4)
  1871  	go func() {
  1872  		for {
  1873  			frame := <-frames
  1874  			if frame.IsHeartbeat() {
  1875  				continue
  1876  			}
  1877  
  1878  			if frame.FileEvent == deleteEvent {
  1879  				close(deleteCh)
  1880  				return
  1881  			}
  1882  		}
  1883  	}()
  1884  
  1885  	// Write a few bytes
  1886  	if _, err := f.Write(data[:3]); err != nil {
  1887  		t.Fatalf("write failed: %v", err)
  1888  	}
  1889  
  1890  	framer := sframer.NewStreamFramer(frames, streamHeartbeatRate, streamBatchWindow, streamFrameSize)
  1891  	framer.Run()
  1892  	defer framer.Destroy()
  1893  
  1894  	// Start streaming
  1895  	go func() {
  1896  		if err := c.endpoints.FileSystem.streamFile(
  1897  			context.Background(), 0, streamFile, 0, ad, framer, nil); err != nil {
  1898  			t.Fatalf("stream() failed: %v", err)
  1899  		}
  1900  	}()
  1901  
  1902  	// Sleep a little before deleting. This lets us check if the watch
  1903  	// is working.
  1904  	time.Sleep(1 * time.Duration(testutil.TestMultiplier()) * time.Second)
  1905  	if err := os.Remove(streamFilePath); err != nil {
  1906  		t.Fatalf("delete failed: %v", err)
  1907  	}
  1908  
  1909  	select {
  1910  	case <-deleteCh:
  1911  	case <-time.After(10 * time.Duration(testutil.TestMultiplier()) * streamBatchWindow):
  1912  		t.Fatalf("did not receive delete")
  1913  	}
  1914  }
  1915  
  1916  func TestFS_logsImpl_NoFollow(t *testing.T) {
  1917  	t.Parallel()
  1918  
  1919  	c := TestClient(t, nil)
  1920  	defer c.Shutdown()
  1921  
  1922  	// Get a temp alloc dir and create the log dir
  1923  	ad := tempAllocDir(t)
  1924  	defer os.RemoveAll(ad.AllocDir)
  1925  
  1926  	logDir := filepath.Join(ad.SharedDir, allocdir.LogDirName)
  1927  	if err := os.MkdirAll(logDir, 0777); err != nil {
  1928  		t.Fatalf("Failed to make log dir: %v", err)
  1929  	}
  1930  
  1931  	// Create a series of log files in the temp dir
  1932  	task := "foo"
  1933  	logType := "stdout"
  1934  	expected := []byte("012")
  1935  	for i := 0; i < 3; i++ {
  1936  		logFile := fmt.Sprintf("%s.%s.%d", task, logType, i)
  1937  		logFilePath := filepath.Join(logDir, logFile)
  1938  		err := ioutil.WriteFile(logFilePath, expected[i:i+1], 777)
  1939  		if err != nil {
  1940  			t.Fatalf("Failed to create file: %v", err)
  1941  		}
  1942  	}
  1943  
  1944  	// Start the reader
  1945  	resultCh := make(chan struct{})
  1946  	frames := make(chan *sframer.StreamFrame, 4)
  1947  	var received []byte
  1948  	go func() {
  1949  		for {
  1950  			frame, ok := <-frames
  1951  			if !ok {
  1952  				return
  1953  			}
  1954  
  1955  			if frame.IsHeartbeat() {
  1956  				continue
  1957  			}
  1958  
  1959  			received = append(received, frame.Data...)
  1960  			if reflect.DeepEqual(received, expected) {
  1961  				close(resultCh)
  1962  				return
  1963  			}
  1964  		}
  1965  	}()
  1966  
  1967  	// Start streaming logs
  1968  	go func() {
  1969  		if err := c.endpoints.FileSystem.logsImpl(
  1970  			context.Background(), false, false, 0,
  1971  			OriginStart, task, logType, ad, frames); err != nil {
  1972  			t.Fatalf("logs() failed: %v", err)
  1973  		}
  1974  	}()
  1975  
  1976  	select {
  1977  	case <-resultCh:
  1978  	case <-time.After(10 * time.Duration(testutil.TestMultiplier()) * streamBatchWindow):
  1979  		t.Fatalf("did not receive data: got %q", string(received))
  1980  	}
  1981  }
  1982  
  1983  func TestFS_logsImpl_Follow(t *testing.T) {
  1984  	t.Parallel()
  1985  
  1986  	c := TestClient(t, nil)
  1987  	defer c.Shutdown()
  1988  
  1989  	// Get a temp alloc dir and create the log dir
  1990  	ad := tempAllocDir(t)
  1991  	defer os.RemoveAll(ad.AllocDir)
  1992  
  1993  	logDir := filepath.Join(ad.SharedDir, allocdir.LogDirName)
  1994  	if err := os.MkdirAll(logDir, 0777); err != nil {
  1995  		t.Fatalf("Failed to make log dir: %v", err)
  1996  	}
  1997  
  1998  	// Create a series of log files in the temp dir
  1999  	task := "foo"
  2000  	logType := "stdout"
  2001  	expected := []byte("012345")
  2002  	initialWrites := 3
  2003  
  2004  	writeToFile := func(index int, data []byte) {
  2005  		logFile := fmt.Sprintf("%s.%s.%d", task, logType, index)
  2006  		logFilePath := filepath.Join(logDir, logFile)
  2007  		err := ioutil.WriteFile(logFilePath, data, 777)
  2008  		if err != nil {
  2009  			t.Fatalf("Failed to create file: %v", err)
  2010  		}
  2011  	}
  2012  	for i := 0; i < initialWrites; i++ {
  2013  		writeToFile(i, expected[i:i+1])
  2014  	}
  2015  
  2016  	// Start the reader
  2017  	firstResultCh := make(chan struct{})
  2018  	fullResultCh := make(chan struct{})
  2019  	frames := make(chan *sframer.StreamFrame, 4)
  2020  	var received []byte
  2021  	go func() {
  2022  		for {
  2023  			frame, ok := <-frames
  2024  			if !ok {
  2025  				return
  2026  			}
  2027  
  2028  			if frame.IsHeartbeat() {
  2029  				continue
  2030  			}
  2031  
  2032  			received = append(received, frame.Data...)
  2033  			if reflect.DeepEqual(received, expected[:initialWrites]) {
  2034  				close(firstResultCh)
  2035  			} else if reflect.DeepEqual(received, expected) {
  2036  				close(fullResultCh)
  2037  				return
  2038  			}
  2039  		}
  2040  	}()
  2041  
  2042  	// Start streaming logs
  2043  	go c.endpoints.FileSystem.logsImpl(
  2044  		context.Background(), true, false, 0,
  2045  		OriginStart, task, logType, ad, frames)
  2046  
  2047  	select {
  2048  	case <-firstResultCh:
  2049  	case <-time.After(10 * time.Duration(testutil.TestMultiplier()) * streamBatchWindow):
  2050  		t.Fatalf("did not receive data: got %q", string(received))
  2051  	}
  2052  
  2053  	// We got the first chunk of data, write out the rest to the next file
  2054  	// at an index much ahead to check that it is following and detecting
  2055  	// skips
  2056  	skipTo := initialWrites + 10
  2057  	writeToFile(skipTo, expected[initialWrites:])
  2058  
  2059  	select {
  2060  	case <-fullResultCh:
  2061  	case <-time.After(10 * time.Duration(testutil.TestMultiplier()) * streamBatchWindow):
  2062  		t.Fatalf("did not receive data: got %q", string(received))
  2063  	}
  2064  }