1 // Copyright (C) The Arvados Authors. All rights reserved.
3 // SPDX-License-Identifier: AGPL-3.0
11 "git.curoverse.com/arvados.git/lib/cloud"
12 "git.curoverse.com/arvados.git/lib/dispatchcloud/test"
13 "git.curoverse.com/arvados.git/sdk/go/arvados"
14 "github.com/Sirupsen/logrus"
15 check "gopkg.in/check.v1"
18 const GiB arvados.ByteSize = 1 << 30
20 var _ = check.Suite(&PoolSuite{})
22 type PoolSuite struct{}
24 func (suite *PoolSuite) SetUpSuite(c *check.C) {
25 logrus.StandardLogger().SetLevel(logrus.DebugLevel)
28 func (suite *PoolSuite) TestStartContainer(c *check.C) {
29 // TODO: use an instanceSet stub with an SSH server
32 func (suite *PoolSuite) TestVerifyHostKey(c *check.C) {
33 // TODO: use an instanceSet stub with an SSH server
36 func (suite *PoolSuite) TestCreateUnallocShutdown(c *check.C) {
37 lameInstanceSet := &test.LameInstanceSet{Hold: make(chan bool)}
38 type1 := arvados.InstanceType{Name: "a1s", ProviderType: "a1.small", VCPUs: 1, RAM: 1 * GiB, Price: .01}
39 type2 := arvados.InstanceType{Name: "a2m", ProviderType: "a2.medium", VCPUs: 2, RAM: 2 * GiB, Price: .02}
41 logger: logrus.StandardLogger(),
42 newExecutor: func(cloud.Instance) Executor { return &stubExecutor{} },
43 instanceSet: lameInstanceSet,
44 instanceTypes: arvados.InstanceTypeMap{
49 notify := pool.Subscribe()
50 defer pool.Unsubscribe(notify)
51 notify2 := pool.Subscribe()
52 defer pool.Unsubscribe(notify2)
54 c.Check(pool.Unallocated()[type1], check.Equals, 0)
55 c.Check(pool.Unallocated()[type2], check.Equals, 0)
59 c.Check(pool.Unallocated()[type1], check.Equals, 1)
60 c.Check(pool.Unallocated()[type2], check.Equals, 2)
61 // Unblock the pending Create calls and (before calling Sync!)
62 // wait for the pool to process the returned instances.
63 go lameInstanceSet.Release(3)
64 suite.wait(c, pool, notify, func() bool {
65 list, err := lameInstanceSet.Instances(nil)
66 return err == nil && len(list) == 3
69 c.Check(pool.Unallocated()[type1], check.Equals, 1)
70 c.Check(pool.Unallocated()[type2], check.Equals, 2)
71 pool.getInstancesAndSync()
72 c.Check(pool.Unallocated()[type1], check.Equals, 1)
73 c.Check(pool.Unallocated()[type2], check.Equals, 2)
75 c.Check(pool.Shutdown(type2), check.Equals, true)
76 suite.wait(c, pool, notify, func() bool {
77 return pool.Unallocated()[type1] == 1 && pool.Unallocated()[type2] == 1
79 c.Check(pool.Shutdown(type2), check.Equals, true)
80 suite.wait(c, pool, notify, func() bool {
81 return pool.Unallocated()[type1] == 1 && pool.Unallocated()[type2] == 0
83 c.Check(pool.Shutdown(type2), check.Equals, false)
85 // Consume any waiting notifications to ensure the
86 // next one we get is from Shutdown.
94 c.Check(pool.Shutdown(type1), check.Equals, true)
95 suite.wait(c, pool, notify, func() bool {
96 return pool.Unallocated()[type1] == 0 && pool.Unallocated()[type2] == 0
100 case <-time.After(time.Second):
101 c.Error("notify did not receive")
103 go lameInstanceSet.Release(3) // unblock Destroy calls
106 func (suite *PoolSuite) wait(c *check.C, pool *Pool, notify <-chan struct{}, ready func() bool) {
107 timeout := time.NewTimer(time.Second).C
116 c.Check(ready(), check.Equals, true)
119 type stubExecutor struct{}
121 func (*stubExecutor) SetTarget(cloud.ExecutorTarget) {}
123 func (*stubExecutor) Execute(cmd string, stdin io.Reader) ([]byte, []byte, error) {
127 func (*stubExecutor) Close() {}