Skip to content

Commit

Permalink
Use HTTP path prefix of TCP connections to match Docker context behavior
Browse files Browse the repository at this point in the history
Signed-off-by: Ben Krieger <[email protected]>
  • Loading branch information
ben-krieger committed Aug 23, 2024
1 parent 6011eac commit 6c68f4a
Show file tree
Hide file tree
Showing 2 changed files with 113 additions and 1 deletion.
7 changes: 6 additions & 1 deletion pkg/bindings/connection.go
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,12 @@ func (c *Connection) DoRequest(ctx context.Context, httpBody io.Reader, httpMeth
params[i+1] = url.PathEscape(pv)
}

uri := fmt.Sprintf("http://d/v%s/libpod"+endpoint, params...)
baseURL := "http://d"
if c.URI.Scheme == "tcp" {
// Allow path prefixes for tcp connections to match Docker behavior
baseURL = "http://" + c.URI.Host + c.URI.Path
}
uri := fmt.Sprintf(baseURL+"/v%s/libpod"+endpoint, params...)
logrus.Debugf("DoRequest Method: %s URI: %v", httpMethod, uri)

req, err := http.NewRequestWithContext(ctx, httpMethod, uri, httpBody)
Expand Down
107 changes: 107 additions & 0 deletions test/e2e/system_connection_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,18 @@
package integration

import (
"context"
"fmt"
"net"
"net/http"
"net/http/httputil"
"net/url"
"os"
"os/exec"
"os/user"
"path/filepath"
"strings"
"time"

. "github.com/containers/podman/v5/test/utils"
. "github.com/onsi/ginkgo/v2"
Expand Down Expand Up @@ -119,6 +125,22 @@ QA-UDS1 unix:///run/user/podman/podman.sock false true
Expect(session.OutputToString()).To(Equal("QA-TCP tcp://localhost:8888 true true"))
})

It("add tcp to reverse proxy path", func() {
cmd := []string{"system", "connection", "add",
"QA-TCP-RP",
"tcp://localhost:8888/reverse/proxy/path/prefix",
}
session := podmanTest.Podman(cmd)
session.WaitWithDefaultTimeout()
Expect(session).Should(ExitCleanly())
Expect(session.Out.Contents()).Should(BeEmpty())

session = podmanTest.Podman(systemConnectionListCmd)
session.WaitWithDefaultTimeout()
Expect(session).Should(ExitCleanly())
Expect(session.OutputToString()).To(Equal("QA-TCP-RP tcp://localhost:8888/reverse/proxy/path/prefix true true"))
})

It("add to new farm", func() {
cmd := []string{"system", "connection", "add",
"--default",
Expand Down Expand Up @@ -315,6 +337,91 @@ qe ssh://[email protected]:2222/run/podman/podman.sock ~/.ssh/id_rsa false true
})
})

Context("with running API service", func() {
BeforeEach(func() {
SkipIfNotRemote("requires podman API service")
})

It("add tcp:// connection with reverse proxy path", func() {
// Create a reverse proxy to the podman socket using path prefix
const pathPrefix = "/reverse/proxy/path/prefix"
proxyGotUsed := false
proxy := http.NewServeMux()
proxy.Handle(pathPrefix+"/", &httputil.ReverseProxy{
Rewrite: func(pr *httputil.ProxyRequest) {
proxyGotUsed = true
pr.Out.URL.Path = strings.TrimPrefix(pr.Out.URL.Path, pathPrefix)
pr.Out.URL.RawPath = strings.TrimPrefix(pr.Out.URL.RawPath, pathPrefix)
baseURL, _ := url.Parse("http://d")
pr.SetURL(baseURL)
},
Transport: &http.Transport{
DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
By("Proxying to " + podmanTest.RemoteSocket)
url, err := url.Parse(podmanTest.RemoteSocket)
if err != nil {
return nil, err
}
return (&net.Dialer{}).DialContext(ctx, "unix", url.Path)
},
},
})
srv := &http.Server{
Handler: proxy,
ReadHeaderTimeout: time.Second,
}

// Serve the reverse proxy on a random port
lis, err := net.Listen("tcp", "127.0.0.1:0")
Expect(err).ToNot(HaveOccurred())
defer lis.Close()

defer srv.Close()
go func() {
defer GinkgoRecover()
Expect(srv.Serve(lis)).To(MatchError(http.ErrServerClosed))
}()

connectionURL := "tcp://" + lis.Addr().String() + pathPrefix

cmd := exec.Command(podmanTest.RemotePodmanBinary,
"system", "connection", "add",
"--default", "QA", connectionURL,
)
session, err := Start(cmd, GinkgoWriter, GinkgoWriter)
Expect(err).ToNot(HaveOccurred(), fmt.Sprintf("%q failed to execute", podmanTest.RemotePodmanBinary))
Eventually(session, DefaultWaitTimeout).Should(Exit(0))
Expect(session.Out.Contents()).Should(BeEmpty())
Expect(session.Err.Contents()).Should(BeEmpty())

Expect(proxyGotUsed).To(BeFalse())
cmd = exec.Command(podmanTest.RemotePodmanBinary,
"--connection", "QA", "ps")
session, err = Start(cmd, GinkgoWriter, GinkgoWriter)
Expect(err).ToNot(HaveOccurred(), fmt.Sprintf("%q failed to execute", podmanTest.RemotePodmanBinary))
Eventually(session, DefaultWaitTimeout).Should(Exit(0))
Expect(session.Out.Contents()).Should(Equal([]byte(`CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
`)))
Expect(session.Err.Contents()).Should(BeEmpty())
Expect(proxyGotUsed).To(BeTrue())

// export the container_host env var and try again
proxyGotUsed = false
err = os.Setenv("CONTAINER_HOST", connectionURL)
Expect(err).ToNot(HaveOccurred())
defer os.Unsetenv("CONTAINER_HOST")

cmd = exec.Command(podmanTest.RemotePodmanBinary, "ps")
session, err = Start(cmd, GinkgoWriter, GinkgoWriter)
Expect(err).ToNot(HaveOccurred(), fmt.Sprintf("%q failed to execute", podmanTest.RemotePodmanBinary))
Eventually(session, DefaultWaitTimeout).Should(Exit(0))
Expect(session.Out.Contents()).Should(Equal([]byte(`CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
`)))
Expect(session.Err.Contents()).Should(BeEmpty())
Expect(proxyGotUsed).To(BeTrue())
})
})

Context("sshd and API services required", func() {
BeforeEach(func() {
// These tests are unique in as much as they require podman, podman-remote, systemd and sshd.
Expand Down

0 comments on commit 6c68f4a

Please sign in to comment.