1 // Copyright (C) The Arvados Authors. All rights reserved.
3 // SPDX-License-Identifier: AGPL-3.0
14 "git.arvados.org/arvados.git/lib/cloud"
15 "git.arvados.org/arvados.git/lib/dispatchcloud/sshexecutor"
16 "git.arvados.org/arvados.git/sdk/go/arvados"
17 "git.arvados.org/arvados.git/sdk/go/ctxlog"
18 "golang.org/x/crypto/ssh"
19 check "gopkg.in/check.v1"
22 func Test(t *testing.T) {
28 var _ = check.Suite(&suite{})
30 func (*suite) TestCreateListExecDestroy(c *check.C) {
31 logger := ctxlog.TestLogger(c)
32 is, err := Driver.InstanceSet(json.RawMessage("{}"), "testInstanceSetID", cloud.SharedResourceTags{"sharedTag": "sharedTagValue"}, logger)
33 c.Assert(err, check.IsNil)
35 clientRSAKey, err := rsa.GenerateKey(rand.Reader, 1024)
36 c.Assert(err, check.IsNil)
37 clientSSHKey, err := ssh.NewSignerFromKey(clientRSAKey)
38 c.Assert(err, check.IsNil)
39 clientSSHPubKey, err := ssh.NewPublicKey(clientRSAKey.Public())
40 c.Assert(err, check.IsNil)
42 it := arvados.InstanceType{
44 ProviderType: "localhost",
49 // First call to Create should succeed, and the returned
50 // instance's SSH target address should be available in << 1s.
51 inst, err := is.Create(it, "testImageID", cloud.InstanceTags{"instanceTag": "instanceTagValue"}, "testInitCommand", clientSSHPubKey)
52 c.Assert(err, check.IsNil)
53 for deadline := time.Now().Add(time.Second); inst.Address() == ""; time.Sleep(time.Second / 100) {
54 if deadline.Before(time.Now()) {
59 // Another call to Create should fail with a quota error.
60 inst2, err := is.Create(it, "testImageID", cloud.InstanceTags{"instanceTag": "instanceTagValue"}, "testInitCommand", clientSSHPubKey)
61 c.Check(inst2, check.IsNil)
62 qerr, ok := err.(cloud.QuotaError)
63 if c.Check(ok, check.Equals, true, check.Commentf("expect cloud.QuotaError, got %#v", err)) {
64 c.Check(qerr.IsQuotaError(), check.Equals, true)
67 // Instance list should now have one entry, for the new
69 list, err := is.Instances(nil)
70 c.Assert(err, check.IsNil)
71 c.Assert(list, check.HasLen, 1)
73 c.Check(inst.String(), check.Equals, "localhost")
75 // Instance's SSH server should execute shell commands.
76 exr := sshexecutor.New(inst)
77 exr.SetSigners(clientSSHKey)
79 stdout, stderr, err := exr.Execute(nil, "echo ok", nil)
80 c.Check(err, check.IsNil)
81 c.Check(string(stdout), check.Equals, "ok\n")
82 c.Check(string(stderr), check.Equals, "")
84 // SSH server should propagate stderr and non-zero exit
86 stdout, stderr, err = exr.Execute(nil, "echo fail && echo -n fail2 >&2 && false", nil)
87 c.Check(err, check.FitsTypeOf, &ssh.ExitError{})
88 c.Check(string(stdout), check.Equals, "fail\n")
89 c.Check(string(stderr), check.Equals, "fail2")
91 // SSH server should strip "sudo" from the front of the
93 withoutsudo, _, err := exr.Execute(nil, "whoami", nil)
94 c.Check(err, check.IsNil)
95 withsudo, _, err := exr.Execute(nil, "sudo whoami", nil)
96 c.Check(err, check.IsNil)
97 c.Check(string(withsudo), check.Equals, string(withoutsudo))
99 // SSH server should reject keys other than the one whose
100 // public key we passed to Create.
101 badRSAKey, err := rsa.GenerateKey(rand.Reader, 1024)
102 c.Assert(err, check.IsNil)
103 badSSHKey, err := ssh.NewSignerFromKey(badRSAKey)
104 c.Assert(err, check.IsNil)
105 // Create a new executor here, otherwise Execute would reuse
106 // the existing connection instead of authenticating with
108 exr = sshexecutor.New(inst)
109 exr.SetSigners(badSSHKey)
110 stdout, stderr, err = exr.Execute(nil, "true", nil)
111 c.Check(err, check.ErrorMatches, `.*unable to authenticate.*`)
113 // Destroying the instance causes it to disappear from the
114 // list, and allows us to create one more.
116 c.Check(err, check.IsNil)
117 list, err = is.Instances(nil)
118 c.Assert(err, check.IsNil)
119 c.Assert(list, check.HasLen, 0)
120 _, err = is.Create(it, "testImageID", cloud.InstanceTags{"instanceTag": "instanceTagValue"}, "testInitCommand", clientSSHPubKey)
121 c.Check(err, check.IsNil)
122 _, err = is.Create(it, "testImageID", cloud.InstanceTags{"instanceTag": "instanceTagValue"}, "testInitCommand", clientSSHPubKey)
123 c.Check(err, check.NotNil)
124 list, err = is.Instances(nil)
125 c.Assert(err, check.IsNil)
126 c.Assert(list, check.HasLen, 1)