diff --git a/api/exec/compose_wrapper.go b/api/exec/compose_wrapper.go index ad49f227f..995f69b43 100644 --- a/api/exec/compose_wrapper.go +++ b/api/exec/compose_wrapper.go @@ -4,9 +4,6 @@ import ( "bytes" "errors" "fmt" - "log" - "net" - "net/http" "os" "os/exec" "path" @@ -67,39 +64,15 @@ func (w *ComposeWrapper) command(command []string, stack *portainer.Stack, endpo tunnel := w.proxyManager.GetReverseTunnel(endpoint) options = append(options, "-H", fmt.Sprintf("http://127.0.0.1:%d", tunnel.Port)) } else if endpoint.URL != "" && !(strings.HasPrefix(endpoint.URL, "unix://") || strings.HasPrefix(endpoint.URL, "npipe://")) { - proxy, err := w.proxyManager.CreateAndRegisterComposeEndpointProxy(endpoint) + + proxy, err := w.proxyManager.CreateComposeProxyServer(endpoint) if err != nil { return nil, err } - listener, err := net.Listen("tcp", ":0") - if err != nil { - return nil, err - } + defer proxy.Close() - server := http.Server{ - Handler: proxy, - } - - shutdownChan := make(chan error, 1) - port := listener.Addr().(*net.TCPAddr).Port - go func() { - - log.Printf("Starting Proxy server on %s...\n", fmt.Sprintf("http://127.0.0.1:%d", port)) - - err := server.Serve(listener) - log.Printf("Proxy Server exited with '%v' error\n", err) - - if err != http.ErrServerClosed { - log.Printf("Put '%v' error returned by Proxy Server to shutdown channel\n", err) - shutdownChan <- err - } - }() - - defer server.Close() - defer w.proxyManager.DeleteEndpointProxy(endpoint) - - options = append(options, "-H", fmt.Sprintf("http://127.0.0.1:%d", port)) + options = append(options, "-H", fmt.Sprintf("http://127.0.0.1:%d", proxy.Port)) } } diff --git a/api/http/proxy/factory/docker_compose.go b/api/http/proxy/factory/docker_compose.go index 9e7d05830..29ff0a2a3 100644 --- a/api/http/proxy/factory/docker_compose.go +++ b/api/http/proxy/factory/docker_compose.go @@ -1,6 +1,9 @@ package factory import ( + "fmt" + "log" + "net" "net/http" "net/url" @@ -9,7 +12,13 @@ import ( "github.com/portainer/portainer/api/http/proxy/factory/dockercompose" ) -func (factory *ProxyFactory) NewDockerComposeAgentProxy(endpoint *portainer.Endpoint) (http.Handler, error) { +// ProxyServer provide an extedned proxy with a local server to forward requests +type ProxyServer struct { + server http.Server + Port int +} + +func (factory *ProxyFactory) NewDockerComposeAgentProxy(endpoint *portainer.Endpoint) (*ProxyServer, error) { endpointURL, err := url.Parse(endpoint.URL) if err != nil { @@ -33,9 +42,45 @@ func (factory *ProxyFactory) NewDockerComposeAgentProxy(endpoint *portainer.Endp proxy.Transport = dockercompose.NewAgentTransport(factory.signatureService, httpTransport) - return proxy, nil + proxyServer := &ProxyServer{ + http.Server{ + Handler: proxy, + }, + 0, + } + + return proxyServer, proxyServer.Start() } func (factory *ProxyFactory) GetReverseTunnel(endpoint *portainer.Endpoint) *portainer.TunnelDetails { return factory.reverseTunnelService.GetTunnelDetails(endpoint.ID) } + +func (proxy *ProxyServer) Start() error { + listener, err := net.Listen("tcp", ":0") + if err != nil { + return err + } + + shutdownChan := make(chan error, 1) + proxy.Port = listener.Addr().(*net.TCPAddr).Port + go func() { + + log.Printf("Starting Proxy server on %s...\n", fmt.Sprintf("http://127.0.0.1:%d", proxy.Port)) + + err := proxy.server.Serve(listener) + log.Printf("Proxy Server exited with '%v' error\n", err) + + if err != http.ErrServerClosed { + log.Printf("Put '%v' error returned by Proxy Server to shutdown channel\n", proxy.Port) + shutdownChan <- err + } + }() + + return nil +} + +// Close the server proxy +func (proxy *ProxyServer) Close() { + proxy.server.Close() +} diff --git a/api/http/proxy/manager.go b/api/http/proxy/manager.go index 733421f77..359be7493 100644 --- a/api/http/proxy/manager.go +++ b/api/http/proxy/manager.go @@ -48,16 +48,10 @@ func (manager *Manager) CreateAndRegisterEndpointProxy(endpoint *portainer.Endpo return proxy, nil } -// CreateAndRegisterComposeEndpointProxy creates a new HTTP reverse proxy based on endpoint properties and and adds it to the registered proxies. +// CreateComposeProxyServer creates a new HTTP reverse proxy based on endpoint properties and and adds it to the registered proxies. // It can also be used to create a new HTTP reverse proxy and replace an already registered proxy. -func (manager *Manager) CreateAndRegisterComposeEndpointProxy(endpoint *portainer.Endpoint) (http.Handler, error) { - proxy, err := manager.proxyFactory.NewDockerComposeAgentProxy(endpoint) - if err != nil { - return nil, err - } - - manager.endpointProxies.Set(fmt.Sprint(endpoint.ID), proxy) - return proxy, nil +func (manager *Manager) CreateComposeProxyServer(endpoint *portainer.Endpoint) (*factory.ProxyServer, error) { + return manager.proxyFactory.NewDockerComposeAgentProxy(endpoint) } // GetReverseTunnel returns the tunnel associated with the edge endpoint