From 3cdc942884fe529b2ba973a20c92029aa55e6735 Mon Sep 17 00:00:00 2001 From: Tom Clegg Date: Sun, 9 May 2021 01:08:13 -0400 Subject: [PATCH] 17657: Test port forwarding using OpenSSH client. Arvados-DCO-1.1-Signed-off-by: Tom Clegg --- cmd/arvados-client/container_gateway_test.go | 84 ++++++++++++++++++++ 1 file changed, 84 insertions(+) diff --git a/cmd/arvados-client/container_gateway_test.go b/cmd/arvados-client/container_gateway_test.go index 97a615e4ba..62303eab63 100644 --- a/cmd/arvados-client/container_gateway_test.go +++ b/cmd/arvados-client/container_gateway_test.go @@ -10,14 +10,21 @@ import ( "crypto/hmac" "crypto/sha256" "fmt" + "io/ioutil" + "net" + "net/http" "net/url" "os" "os/exec" + "strings" + "sync" + "time" "git.arvados.org/arvados.git/lib/controller/rpc" "git.arvados.org/arvados.git/lib/crunchrun" "git.arvados.org/arvados.git/sdk/go/arvados" "git.arvados.org/arvados.git/sdk/go/arvadostest" + "git.arvados.org/arvados.git/sdk/go/httpserver" check "gopkg.in/check.v1" ) @@ -47,6 +54,10 @@ func (s *ClientSuite) TestShellGateway(c *check.C) { ContainerUUID: uuid, Address: "0.0.0.0:0", AuthSecret: authSecret, + // Just forward connections to localhost instead of a + // container, so we can test without running a + // container. + ContainerIPAddress: func() (string, error) { return "0.0.0.0", nil }, } err := gw.Start() c.Assert(err, check.IsNil) @@ -79,4 +90,77 @@ func (s *ClientSuite) TestShellGateway(c *check.C) { c.Check(cmd.Run(), check.NotNil) c.Log(stderr.String()) c.Check(stderr.String(), check.Matches, `(?ms).*(No such container: theperthcountyconspiracy|exec: \"docker\": executable file not found in \$PATH).*`) + + // Set up an http server, and try using "arvados-client shell" + // to forward traffic to it. + httpTarget := &httpserver.Server{} + httpTarget.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + c.Logf("httpTarget.Handler: incoming request: %s %s", r.Method, r.URL) + if r.URL.Path == "/foo" { + fmt.Fprintln(w, "bar baz") + } else { + w.WriteHeader(http.StatusNotFound) + } + }) + err = httpTarget.Start() + c.Assert(err, check.IsNil) + + ln, err := net.Listen("tcp", ":0") + c.Assert(err, check.IsNil) + _, forwardedPort, _ := net.SplitHostPort(ln.Addr().String()) + ln.Close() + + stdout.Reset() + stderr.Reset() + ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(10*time.Second)) + defer cancel() + cmd = exec.CommandContext(ctx, + "go", "run", ".", "shell", uuid, + "-L", forwardedPort+":"+httpTarget.Addr, + "-o", "controlpath=none", + "-o", "userknownhostsfile="+c.MkDir()+"/known_hosts", + "-N", + ) + c.Logf("cmd.Args: %s", cmd.Args) + cmd.Env = append(cmd.Env, os.Environ()...) + cmd.Env = append(cmd.Env, "ARVADOS_API_TOKEN="+arvadostest.ActiveTokenV2) + cmd.Stdout = &stdout + cmd.Stderr = &stderr + go cmd.Run() + + forwardedURL := fmt.Sprintf("http://localhost:%s/foo", forwardedPort) + + for range time.NewTicker(time.Second / 20).C { + resp, err := http.Get(forwardedURL) + if err != nil { + if !strings.Contains(err.Error(), "connect") { + c.Fatal(err) + } else if ctx.Err() != nil { + c.Fatal("timed out") + } + // Retry until OpenSSH starts listening + continue + } + c.Check(resp.StatusCode, check.Equals, http.StatusOK) + body, err := ioutil.ReadAll(resp.Body) + c.Check(err, check.IsNil) + c.Check(string(body), check.Equals, "bar baz\n") + break + } + + var wg sync.WaitGroup + for i := 0; i < 10; i++ { + wg.Add(1) + go func() { + defer wg.Done() + resp, err := http.Get(forwardedURL) + if !c.Check(err, check.IsNil) { + return + } + body, err := ioutil.ReadAll(resp.Body) + c.Check(err, check.IsNil) + c.Check(string(body), check.Equals, "bar baz\n") + }() + } + wg.Wait() } -- 2.30.2