From 4141d8fe5da596e7ee1eec217378aeb684d0a99e Mon Sep 17 00:00:00 2001 From: Tonis Tiigi Date: Tue, 27 Jun 2017 22:42:28 -0700 Subject: [PATCH] add test for filesync path filtering and testutil helper Signed-off-by: Tonis Tiigi --- client/session/filesync/filesync_test.go | 71 ++++++++++++++++++++++++ client/session/manager.go | 21 ++++++- client/session/testutil/testutil.go | 70 +++++++++++++++++++++++ 3 files changed, 159 insertions(+), 3 deletions(-) create mode 100644 client/session/filesync/filesync_test.go create mode 100644 client/session/testutil/testutil.go diff --git a/client/session/filesync/filesync_test.go b/client/session/filesync/filesync_test.go new file mode 100644 index 0000000000..b48c08b826 --- /dev/null +++ b/client/session/filesync/filesync_test.go @@ -0,0 +1,71 @@ +package filesync + +import ( + "context" + "io/ioutil" + "path/filepath" + "testing" + + "github.com/docker/docker/client/session" + "github.com/docker/docker/client/session/testutil" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "golang.org/x/sync/errgroup" +) + +func TestFileSyncIncludePatterns(t *testing.T) { + tmpDir, err := ioutil.TempDir("", "fsynctest") + require.NoError(t, err) + + destDir, err := ioutil.TempDir("", "fsynctest") + require.NoError(t, err) + + err = ioutil.WriteFile(filepath.Join(tmpDir, "foo"), []byte("content1"), 0600) + require.NoError(t, err) + + err = ioutil.WriteFile(filepath.Join(tmpDir, "bar"), []byte("content2"), 0600) + require.NoError(t, err) + + s, err := session.NewSession("foo", "bar") + require.NoError(t, err) + + m, err := session.NewManager() + require.NoError(t, err) + + fs := NewFSSyncProvider(tmpDir, nil) + s.Allow(fs) + + dialer := session.Dialer(testutil.TestStream(testutil.Handler(m.HandleConn))) + + g, ctx := errgroup.WithContext(context.Background()) + + g.Go(func() error { + return s.Run(ctx, dialer) + }) + + g.Go(func() (reterr error) { + c, err := m.Get(ctx, s.UUID()) + if err != nil { + return err + } + if err := FSSync(ctx, c, FSSendRequestOpt{ + DestDir: destDir, + IncludePatterns: []string{"ba*"}, + }); err != nil { + return err + } + + _, err = ioutil.ReadFile(filepath.Join(destDir, "foo")) + assert.Error(t, err) + + dt, err := ioutil.ReadFile(filepath.Join(destDir, "bar")) + if err != nil { + return err + } + assert.Equal(t, "content2", string(dt)) + return s.Close() + }) + + err = g.Wait() + require.NoError(t, err) +} diff --git a/client/session/manager.go b/client/session/manager.go index 023e850301..9523e6f317 100644 --- a/client/session/manager.go +++ b/client/session/manager.go @@ -1,6 +1,7 @@ package session import ( + "net" "net/http" "strings" "sync" @@ -49,8 +50,6 @@ func (sm *Manager) HandleHTTPRequest(ctx context.Context, w http.ResponseWriter, } uuid := r.Header.Get(headerSessionUUID) - name := r.Header.Get(headerSessionName) - sharedKey := r.Header.Get(headerSessionSharedKey) proto := r.Header.Get("Upgrade") @@ -89,9 +88,25 @@ func (sm *Manager) HandleHTTPRequest(ctx context.Context, w http.ResponseWriter, conn.Write([]byte{}) resp.Write(conn) + return sm.handleConn(ctx, conn, r.Header) +} + +// HandleConn handles an incoming raw connection +func (sm *Manager) HandleConn(ctx context.Context, conn net.Conn, opts map[string][]string) error { + sm.mu.Lock() + return sm.handleConn(ctx, conn, opts) +} + +// caller needs to take lock, this function will release it +func (sm *Manager) handleConn(ctx context.Context, conn net.Conn, opts map[string][]string) error { ctx, cancel := context.WithCancel(ctx) defer cancel() + h := http.Header(opts) + uuid := h.Get(headerSessionUUID) + name := h.Get(headerSessionName) + sharedKey := h.Get(headerSessionSharedKey) + ctx, cc, err := grpcClientConn(ctx, conn) if err != nil { sm.mu.Unlock() @@ -111,7 +126,7 @@ func (sm *Manager) HandleHTTPRequest(ctx context.Context, w http.ResponseWriter, supported: make(map[string]struct{}), } - for _, m := range r.Header[headerSessionMethod] { + for _, m := range opts[headerSessionMethod] { c.supported[strings.ToLower(m)] = struct{}{} } sm.sessions[uuid] = c diff --git a/client/session/testutil/testutil.go b/client/session/testutil/testutil.go new file mode 100644 index 0000000000..2e145d9006 --- /dev/null +++ b/client/session/testutil/testutil.go @@ -0,0 +1,70 @@ +package testutil + +import ( + "io" + "net" + "time" + + "github.com/Sirupsen/logrus" + "golang.org/x/net/context" +) + +// Handler is function called to handle incoming connection +type Handler func(ctx context.Context, conn net.Conn, meta map[string][]string) error + +// Dialer is a function for dialing an outgoing connection +type Dialer func(ctx context.Context, proto string, meta map[string][]string) (net.Conn, error) + +// TestStream creates an in memory session dialer for a handler function +func TestStream(handler Handler) Dialer { + s1, s2 := sockPair() + return func(ctx context.Context, proto string, meta map[string][]string) (net.Conn, error) { + go func() { + err := handler(context.TODO(), s1, meta) + if err != nil { + logrus.Error(err) + } + s1.Close() + }() + return s2, nil + } +} + +func sockPair() (*sock, *sock) { + pr1, pw1 := io.Pipe() + pr2, pw2 := io.Pipe() + return &sock{pw1, pr2, pw1}, &sock{pw2, pr1, pw2} +} + +type sock struct { + io.Writer + io.Reader + io.Closer +} + +func (s *sock) LocalAddr() net.Addr { + return dummyAddr{} +} +func (s *sock) RemoteAddr() net.Addr { + return dummyAddr{} +} +func (s *sock) SetDeadline(t time.Time) error { + return nil +} +func (s *sock) SetReadDeadline(t time.Time) error { + return nil +} +func (s *sock) SetWriteDeadline(t time.Time) error { + return nil +} + +type dummyAddr struct { +} + +func (d dummyAddr) Network() string { + return "tcp" +} + +func (d dummyAddr) String() string { + return "localhost" +}