From 0143616ebda7ac26dd3cb894c3a22f1182ffd3db Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Fri, 8 Jul 2022 14:30:53 +0200 Subject: [PATCH 1/2] hiveproxy: handle errors during connection setup This fixes a panic in internal/libdocker, which could occur when hive was interrupted while setting up the proxy. --- hiveproxy/proxy.go | 24 ++++++++++++++++-------- hiveproxy/proxy_test.go | 34 +++++++++++++++++++++++----------- internal/libdocker/proxy.go | 10 +++++++++- 3 files changed, 48 insertions(+), 20 deletions(-) diff --git a/hiveproxy/proxy.go b/hiveproxy/proxy.go index 268170ba8a..be6ad3e502 100644 --- a/hiveproxy/proxy.go +++ b/hiveproxy/proxy.go @@ -117,14 +117,18 @@ func (p *Proxy) launchRPC(stream net.Conn) { // to the backend. // // All communication with the backend runs over the given r,w streams. -func RunFrontend(r io.Reader, w io.WriteCloser, listener net.Listener) *Proxy { - mux, _ := yamux.Client(rwCombo{r, w}, muxcfg) +func RunFrontend(r io.Reader, w io.WriteCloser, listener net.Listener) (*Proxy, error) { p := newProxy(true) + mux, err := yamux.Client(rwCombo{r, w}, muxcfg) + if err != nil { + return nil, err + } // Launch RPC handler. rpcConn, err := mux.Open() if err != nil { - panic(err) + mux.Close() + return nil, err } p.launchRPC(rpcConn) p.rpc.RegisterName("proxy", new(proxyFunctions)) @@ -144,28 +148,32 @@ func RunFrontend(r io.Reader, w io.WriteCloser, listener net.Listener) *Proxy { Transport: transport, } go p.serve(listener) - return p + return p, nil } // RunBackend starts the proxy backend, i.e. the side which handles HTTP requests proxied // by the frontend. // // All communication with the frontend runs over the given r,w streams. -func RunBackend(r io.Reader, w io.WriteCloser, h http.Handler) *Proxy { - mux, _ := yamux.Server(rwCombo{r, w}, muxcfg) +func RunBackend(r io.Reader, w io.WriteCloser, h http.Handler) (*Proxy, error) { p := newProxy(false) + mux, err := yamux.Server(rwCombo{r, w}, muxcfg) + if err != nil { + return nil, err + } // Start RPC client. rpcConn, err := mux.Accept() if err != nil { - panic(err) + mux.Close() + return nil, err } p.launchRPC(rpcConn) // Start HTTP server. p.httpsrv.Handler = h go p.serve(mux) - return p + return p, nil } type rwCombo struct { diff --git a/hiveproxy/proxy_test.go b/hiveproxy/proxy_test.go index 714a0cb260..981ba2de66 100644 --- a/hiveproxy/proxy_test.go +++ b/hiveproxy/proxy_test.go @@ -40,7 +40,7 @@ func TestProxyCheckLive(t *testing.T) { defer tl.Close() // Run CheckLive on the backend side. - addr := tl.l.Addr().(*net.TCPAddr) + addr := tl.lis.Addr().(*net.TCPAddr) if err := p.back.CheckLive(context.Background(), addr); err != nil { t.Fatal("CheckLive did not work:", err) } @@ -80,15 +80,27 @@ func runProxyPair(t *testing.T, h http.Handler) proxyPair { t.Fatal(err) } p.lis = l - frontStarted := make(chan struct{}) + frontStarted := make(chan error, 1) go func() { - p.front = RunFrontend(cr, cw, l) - close(frontStarted) + var err error + p.front, err = RunFrontend(cr, cw, l) + frontStarted <- err }() // Run the backend. - p.back = RunBackend(sr, sw, h) - <-frontStarted + p.back, err = RunBackend(sr, sw, h) + if err != nil { + <-frontStarted + if p.front != nil { + p.front.Close() + } + t.Fatal(err) + } + + if err := <-frontStarted; err != nil { + p.back.Close() + t.Fatal(err) + } return p } @@ -98,8 +110,8 @@ func (p proxyPair) close() { } type testListener struct { - l net.Listener - wg sync.WaitGroup + lis net.Listener + wg sync.WaitGroup } func runTestListener(addr string) (*testListener, error) { @@ -107,21 +119,21 @@ func runTestListener(addr string) (*testListener, error) { if err != nil { return nil, err } - tl := &testListener{l: l} + tl := &testListener{lis: l} tl.wg.Add(1) go tl.acceptLoop() return tl, nil } func (tl *testListener) Close() { - tl.l.Close() + tl.lis.Close() tl.wg.Wait() } func (tl *testListener) acceptLoop() { defer tl.wg.Done() for { - c, err := tl.l.Accept() + c, err := tl.lis.Accept() if err != nil { return } diff --git a/internal/libdocker/proxy.go b/internal/libdocker/proxy.go index 0519d0fd6b..179fe5b403 100644 --- a/internal/libdocker/proxy.go +++ b/internal/libdocker/proxy.go @@ -35,7 +35,15 @@ func (cb *ContainerBackend) ServeAPI(ctx context.Context, h http.Handler) (libhi return nil, err } - proxy := hiveproxy.RunBackend(outR, inW, h) + proxy, err := hiveproxy.RunBackend(outR, inW, h) + if err != nil { + cb.DeleteContainer(id) + return nil, err + } + + // Register proxy in ContainerBackend, so it can be used for CheckLive. + cb.proxy = proxy + srv := &proxyContainer{ cb: cb, containerID: id, From 60ca5d4a126c2206ea628dd271e9e0b344baa289 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Fri, 8 Jul 2022 14:57:07 +0200 Subject: [PATCH 2/2] hiveproxy: rename executable --- hiveproxy/Dockerfile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hiveproxy/Dockerfile b/hiveproxy/Dockerfile index 5de71ab96d..97e66cb869 100644 --- a/hiveproxy/Dockerfile +++ b/hiveproxy/Dockerfile @@ -10,10 +10,10 @@ RUN go mod download # Now build the proxy executable. ADD . /source -RUN go build -o hive-proxy ./tool +RUN go build -o /bin/hiveproxy ./tool # Pull the executable into a fresh image. FROM alpine:latest -COPY --from=builder /source/hive-proxy . +COPY --from=builder /bin/hiveproxy . EXPOSE 8081/tcp -ENTRYPOINT ./hive-proxy --addr :8081 +ENTRYPOINT ./hiveproxy --addr :8081