1 // Copyright (C) The Arvados Authors. All rights reserved.
3 // SPDX-License-Identifier: AGPL-3.0
15 "git.arvados.org/arvados.git/lib/config"
16 "git.arvados.org/arvados.git/lib/crunchrun"
17 "git.arvados.org/arvados.git/sdk/go/arvados"
18 "git.arvados.org/arvados.git/sdk/go/arvadostest"
19 "git.arvados.org/arvados.git/sdk/go/auth"
20 "git.arvados.org/arvados.git/sdk/go/ctxlog"
21 check "gopkg.in/check.v1"
24 var _ = check.Suite(&ContainerGatewaySuite{})
26 type ContainerGatewaySuite struct {
27 cluster *arvados.Cluster
34 func (s *ContainerGatewaySuite) TearDownSuite(c *check.C) {
35 // Undo any changes/additions to the user database so they
36 // don't affect subsequent tests.
37 arvadostest.ResetEnv()
38 c.Check(arvados.NewClientFromEnv().RequestAndDecode(nil, "POST", "database/reset", nil, nil), check.IsNil)
41 func (s *ContainerGatewaySuite) SetUpSuite(c *check.C) {
42 cfg, err := config.NewLoader(nil, ctxlog.TestLogger(c)).Load()
43 c.Assert(err, check.IsNil)
44 s.cluster, err = cfg.GetCluster("")
45 c.Assert(err, check.IsNil)
46 s.localdb = NewConn(s.cluster)
47 s.ctx = auth.NewContext(context.Background(), &auth.Credentials{Tokens: []string{arvadostest.ActiveTokenV2}})
49 s.ctrUUID = arvadostest.QueuedContainerUUID
51 h := hmac.New(sha256.New, []byte(s.cluster.SystemRootToken))
52 fmt.Fprint(h, s.ctrUUID)
53 authKey := fmt.Sprintf("%x", h.Sum(nil))
55 s.gw = &crunchrun.Gateway{
56 DockerContainerID: new(string),
57 ContainerUUID: s.ctrUUID,
59 Address: "localhost:0",
60 Log: ctxlog.TestLogger(c),
62 c.Assert(s.gw.Start(), check.IsNil)
63 rootctx := auth.NewContext(context.Background(), &auth.Credentials{Tokens: []string{s.cluster.SystemRootToken}})
64 _, err = s.localdb.ContainerUpdate(rootctx, arvados.UpdateOptions{
66 Attrs: map[string]interface{}{
67 "state": arvados.ContainerStateLocked}})
68 c.Assert(err, check.IsNil)
69 _, err = s.localdb.ContainerUpdate(rootctx, arvados.UpdateOptions{
71 Attrs: map[string]interface{}{
72 "state": arvados.ContainerStateRunning,
73 "gateway_address": s.gw.Address}})
74 c.Assert(err, check.IsNil)
77 func (s *ContainerGatewaySuite) SetUpTest(c *check.C) {
78 s.cluster.Containers.ShellAccess.Admin = true
79 s.cluster.Containers.ShellAccess.User = true
80 _, err := arvadostest.DB(c, s.cluster).Exec(`update containers set interactive_session_started=$1 where uuid=$2`, false, s.ctrUUID)
81 c.Check(err, check.IsNil)
84 func (s *ContainerGatewaySuite) TestConfig(c *check.C) {
85 for _, trial := range []struct {
91 {true, true, arvadostest.ActiveTokenV2, 0},
92 {true, false, arvadostest.ActiveTokenV2, 503},
93 {false, true, arvadostest.ActiveTokenV2, 0},
94 {false, false, arvadostest.ActiveTokenV2, 503},
95 {true, true, arvadostest.AdminToken, 0},
96 {true, false, arvadostest.AdminToken, 0},
97 {false, true, arvadostest.AdminToken, 403},
98 {false, false, arvadostest.AdminToken, 503},
100 c.Logf("trial %#v", trial)
101 s.cluster.Containers.ShellAccess.Admin = trial.configAdmin
102 s.cluster.Containers.ShellAccess.User = trial.configUser
103 ctx := auth.NewContext(s.ctx, &auth.Credentials{Tokens: []string{trial.sendToken}})
104 sshconn, err := s.localdb.ContainerSSH(ctx, arvados.ContainerSSHOptions{UUID: s.ctrUUID})
105 if trial.errorCode == 0 {
106 if !c.Check(err, check.IsNil) {
109 if !c.Check(sshconn.Conn, check.NotNil) {
114 c.Check(err, check.NotNil)
115 err, ok := err.(interface{ HTTPStatus() int })
116 if c.Check(ok, check.Equals, true) {
117 c.Check(err.HTTPStatus(), check.Equals, trial.errorCode)
123 func (s *ContainerGatewaySuite) TestConnect(c *check.C) {
124 c.Logf("connecting to %s", s.gw.Address)
125 sshconn, err := s.localdb.ContainerSSH(s.ctx, arvados.ContainerSSHOptions{UUID: s.ctrUUID})
126 c.Assert(err, check.IsNil)
127 c.Assert(sshconn.Conn, check.NotNil)
128 defer sshconn.Conn.Close()
130 done := make(chan struct{})
134 // Receive text banner
135 buf := make([]byte, 12)
136 _, err := io.ReadFull(sshconn.Conn, buf)
137 c.Check(err, check.IsNil)
138 c.Check(string(buf), check.Equals, "SSH-2.0-Go\r\n")
141 _, err = sshconn.Conn.Write([]byte("SSH-2.0-Fake\r\n"))
142 c.Check(err, check.IsNil)
145 _, err = io.ReadFull(sshconn.Conn, buf[:4])
146 c.Check(err, check.IsNil)
147 c.Check(buf[:4], check.DeepEquals, []byte{0, 0, 1, 0xfc})
149 // If we can get this far into an SSH handshake...
150 c.Log("success, tunnel is working")
154 case <-time.After(time.Second):
157 ctr, err := s.localdb.ContainerGet(s.ctx, arvados.GetOptions{UUID: s.ctrUUID})
158 c.Check(err, check.IsNil)
159 c.Check(ctr.InteractiveSessionStarted, check.Equals, true)
162 func (s *ContainerGatewaySuite) TestConnectFail(c *check.C) {
163 c.Log("trying with no token")
164 ctx := auth.NewContext(context.Background(), &auth.Credentials{})
165 _, err := s.localdb.ContainerSSH(ctx, arvados.ContainerSSHOptions{UUID: s.ctrUUID})
166 c.Check(err, check.ErrorMatches, `.* 401 .*`)
168 c.Log("trying with anonymous token")
169 ctx = auth.NewContext(context.Background(), &auth.Credentials{Tokens: []string{arvadostest.AnonymousToken}})
170 _, err = s.localdb.ContainerSSH(ctx, arvados.ContainerSSHOptions{UUID: s.ctrUUID})
171 c.Check(err, check.ErrorMatches, `.* 404 .*`)