8015: Expand arv-mount command line tests
[arvados.git] / services / crunch-run / crunchrun_test.go
1 package main
2
3 import (
4         "bytes"
5         "crypto/md5"
6         "encoding/json"
7         "errors"
8         "fmt"
9         "git.curoverse.com/arvados.git/sdk/go/arvadosclient"
10         "git.curoverse.com/arvados.git/sdk/go/keepclient"
11         "git.curoverse.com/arvados.git/sdk/go/manifest"
12         "github.com/curoverse/dockerclient"
13         . "gopkg.in/check.v1"
14         "io"
15         "io/ioutil"
16         "os"
17         "os/exec"
18         "strings"
19         "syscall"
20         "testing"
21         "time"
22 )
23
24 // Gocheck boilerplate
25 func TestCrunchExec(t *testing.T) {
26         TestingT(t)
27 }
28
29 type TestSuite struct{}
30
31 // Gocheck boilerplate
32 var _ = Suite(&TestSuite{})
33
34 type ArvTestClient struct {
35         Total   int64
36         Calls   int
37         Content arvadosclient.Dict
38         ContainerRecord
39         Logs          map[string]*bytes.Buffer
40         WasSetRunning bool
41 }
42
43 type KeepTestClient struct {
44         Called  bool
45         Content []byte
46 }
47
48 var hwManifest = ". 82ab40c24fc8df01798e57ba66795bb1+841216+Aa124ac75e5168396c73c0a18eda641a4f41791c0@569fa8c3 0:841216:9c31ee32b3d15268a0754e8edc74d4f815ee014b693bc5109058e431dd5caea7.tar\n"
49 var hwPDH = "a45557269dcb65a6b78f9ac061c0850b+120"
50 var hwImageId = "9c31ee32b3d15268a0754e8edc74d4f815ee014b693bc5109058e431dd5caea7"
51
52 var otherManifest = ". 68a84f561b1d1708c6baff5e019a9ab3+46+Ae5d0af96944a3690becb1decdf60cc1c937f556d@5693216f 0:46:md5sum.txt\n"
53 var otherPDH = "a3e8f74c6f101eae01fa08bfb4e49b3a+54"
54
55 type TestDockerClient struct {
56         imageLoaded  string
57         stdoutReader io.ReadCloser
58         stderrReader io.ReadCloser
59         stdoutWriter io.WriteCloser
60         stderrWriter io.WriteCloser
61         fn           func(t *TestDockerClient)
62         finish       chan dockerclient.WaitResult
63         stop         chan bool
64         cwd          string
65         env          []string
66 }
67
68 func NewTestDockerClient() *TestDockerClient {
69         t := &TestDockerClient{}
70         t.stdoutReader, t.stdoutWriter = io.Pipe()
71         t.stderrReader, t.stderrWriter = io.Pipe()
72         t.finish = make(chan dockerclient.WaitResult)
73         t.stop = make(chan bool)
74         t.cwd = "/"
75         return t
76 }
77
78 func (t *TestDockerClient) StopContainer(id string, timeout int) error {
79         t.stop <- true
80         return nil
81 }
82
83 func (t *TestDockerClient) InspectImage(id string) (*dockerclient.ImageInfo, error) {
84         if t.imageLoaded == id {
85                 return &dockerclient.ImageInfo{}, nil
86         } else {
87                 return nil, errors.New("")
88         }
89 }
90
91 func (t *TestDockerClient) LoadImage(reader io.Reader) error {
92         _, err := io.Copy(ioutil.Discard, reader)
93         if err != nil {
94                 return err
95         } else {
96                 t.imageLoaded = hwImageId
97                 return nil
98         }
99 }
100
101 func (t *TestDockerClient) CreateContainer(config *dockerclient.ContainerConfig, name string, authConfig *dockerclient.AuthConfig) (string, error) {
102         if config.WorkingDir != "" {
103                 t.cwd = config.WorkingDir
104         }
105         t.env = config.Env
106         return "abcde", nil
107 }
108
109 func (t *TestDockerClient) StartContainer(id string, config *dockerclient.HostConfig) error {
110         if id == "abcde" {
111                 go t.fn(t)
112                 return nil
113         } else {
114                 return errors.New("Invalid container id")
115         }
116 }
117
118 func (t *TestDockerClient) ContainerLogs(id string, options *dockerclient.LogOptions) (io.ReadCloser, error) {
119         if options.Stdout {
120                 return t.stdoutReader, nil
121         }
122         if options.Stderr {
123                 return t.stderrReader, nil
124         }
125         return nil, nil
126 }
127
128 func (t *TestDockerClient) Wait(id string) <-chan dockerclient.WaitResult {
129         return t.finish
130 }
131
132 func (*TestDockerClient) RemoveImage(name string, force bool) ([]*dockerclient.ImageDelete, error) {
133         return nil, nil
134 }
135
136 func (this *ArvTestClient) Create(resourceType string,
137         parameters arvadosclient.Dict,
138         output interface{}) error {
139
140         this.Calls += 1
141         this.Content = parameters
142
143         if resourceType == "logs" {
144                 et := parameters["log"].(arvadosclient.Dict)["event_type"].(string)
145                 if this.Logs == nil {
146                         this.Logs = make(map[string]*bytes.Buffer)
147                 }
148                 if this.Logs[et] == nil {
149                         this.Logs[et] = &bytes.Buffer{}
150                 }
151                 this.Logs[et].Write([]byte(parameters["log"].(arvadosclient.Dict)["properties"].(map[string]string)["text"]))
152         }
153
154         if resourceType == "collections" && output != nil {
155                 mt := parameters["collection"].(arvadosclient.Dict)["manifest_text"].(string)
156                 outmap := output.(*CollectionRecord)
157                 outmap.PortableDataHash = fmt.Sprintf("%x+%d", md5.Sum([]byte(mt)), len(mt))
158         }
159
160         return nil
161 }
162
163 func (this *ArvTestClient) Get(resourceType string, uuid string, parameters arvadosclient.Dict, output interface{}) error {
164         if resourceType == "collections" {
165                 if uuid == hwPDH {
166                         output.(*CollectionRecord).ManifestText = hwManifest
167                 } else if uuid == otherPDH {
168                         output.(*CollectionRecord).ManifestText = otherManifest
169                 }
170         }
171         if resourceType == "containers" {
172                 (*output.(*ContainerRecord)) = this.ContainerRecord
173         }
174         return nil
175 }
176
177 func (this *ArvTestClient) Update(resourceType string, uuid string, parameters arvadosclient.Dict, output interface{}) (err error) {
178
179         this.Content = parameters
180         if resourceType == "containers" {
181                 if parameters["container"].(arvadosclient.Dict)["state"] == "Running" {
182                         this.WasSetRunning = true
183                 }
184
185         }
186         return nil
187 }
188
189 func (this *KeepTestClient) PutHB(hash string, buf []byte) (string, int, error) {
190         this.Content = buf
191         return fmt.Sprintf("%s+%d", hash, len(buf)), len(buf), nil
192 }
193
194 type FileWrapper struct {
195         io.ReadCloser
196         len uint64
197 }
198
199 func (this FileWrapper) Len() uint64 {
200         return this.len
201 }
202
203 func (this *KeepTestClient) ManifestFileReader(m manifest.Manifest, filename string) (keepclient.ReadCloserWithLen, error) {
204         if filename == hwImageId+".tar" {
205                 rdr := ioutil.NopCloser(&bytes.Buffer{})
206                 this.Called = true
207                 return FileWrapper{rdr, 1321984}, nil
208         }
209         return nil, nil
210 }
211
212 func (s *TestSuite) TestLoadImage(c *C) {
213         kc := &KeepTestClient{}
214         docker := NewTestDockerClient()
215         cr := NewContainerRunner(&ArvTestClient{}, kc, docker, "zzzzz-zzzzz-zzzzzzzzzzzzzzz")
216
217         _, err := cr.Docker.RemoveImage(hwImageId, true)
218
219         _, err = cr.Docker.InspectImage(hwImageId)
220         c.Check(err, NotNil)
221
222         cr.ContainerRecord.ContainerImage = hwPDH
223
224         // (1) Test loading image from keep
225         c.Check(kc.Called, Equals, false)
226         c.Check(cr.ContainerConfig.Image, Equals, "")
227
228         err = cr.LoadImage()
229
230         c.Check(err, IsNil)
231         defer func() {
232                 cr.Docker.RemoveImage(hwImageId, true)
233         }()
234
235         c.Check(kc.Called, Equals, true)
236         c.Check(cr.ContainerConfig.Image, Equals, hwImageId)
237
238         _, err = cr.Docker.InspectImage(hwImageId)
239         c.Check(err, IsNil)
240
241         // (2) Test using image that's already loaded
242         kc.Called = false
243         cr.ContainerConfig.Image = ""
244
245         err = cr.LoadImage()
246         c.Check(err, IsNil)
247         c.Check(kc.Called, Equals, false)
248         c.Check(cr.ContainerConfig.Image, Equals, hwImageId)
249
250 }
251
252 type ArvErrorTestClient struct{}
253 type KeepErrorTestClient struct{}
254 type KeepReadErrorTestClient struct{}
255
256 func (this ArvErrorTestClient) Create(resourceType string,
257         parameters arvadosclient.Dict,
258         output interface{}) error {
259         return nil
260 }
261
262 func (this ArvErrorTestClient) Get(resourceType string, uuid string, parameters arvadosclient.Dict, output interface{}) error {
263         return errors.New("ArvError")
264 }
265
266 func (this ArvErrorTestClient) Update(resourceType string, uuid string, parameters arvadosclient.Dict, output interface{}) (err error) {
267         return nil
268 }
269
270 func (this KeepErrorTestClient) PutHB(hash string, buf []byte) (string, int, error) {
271         return "", 0, errors.New("KeepError")
272 }
273
274 func (this KeepErrorTestClient) ManifestFileReader(m manifest.Manifest, filename string) (keepclient.ReadCloserWithLen, error) {
275         return nil, errors.New("KeepError")
276 }
277
278 func (this KeepReadErrorTestClient) PutHB(hash string, buf []byte) (string, int, error) {
279         return "", 0, nil
280 }
281
282 type ErrorReader struct{}
283
284 func (this ErrorReader) Read(p []byte) (n int, err error) {
285         return 0, errors.New("ErrorReader")
286 }
287
288 func (this ErrorReader) Close() error {
289         return nil
290 }
291
292 func (this ErrorReader) Len() uint64 {
293         return 0
294 }
295
296 func (this KeepReadErrorTestClient) ManifestFileReader(m manifest.Manifest, filename string) (keepclient.ReadCloserWithLen, error) {
297         return ErrorReader{}, nil
298 }
299
300 func (s *TestSuite) TestLoadImageArvError(c *C) {
301         // (1) Arvados error
302         cr := NewContainerRunner(ArvErrorTestClient{}, &KeepTestClient{}, nil, "zzzzz-zzzzz-zzzzzzzzzzzzzzz")
303         cr.ContainerRecord.ContainerImage = hwPDH
304
305         err := cr.LoadImage()
306         c.Check(err.Error(), Equals, "While getting container image collection: ArvError")
307 }
308
309 func (s *TestSuite) TestLoadImageKeepError(c *C) {
310         // (2) Keep error
311         docker := NewTestDockerClient()
312         cr := NewContainerRunner(&ArvTestClient{}, KeepErrorTestClient{}, docker, "zzzzz-zzzzz-zzzzzzzzzzzzzzz")
313         cr.ContainerRecord.ContainerImage = hwPDH
314
315         err := cr.LoadImage()
316         c.Check(err.Error(), Equals, "While creating ManifestFileReader for container image: KeepError")
317 }
318
319 func (s *TestSuite) TestLoadImageCollectionError(c *C) {
320         // (3) Collection doesn't contain image
321         cr := NewContainerRunner(&ArvTestClient{}, KeepErrorTestClient{}, nil, "zzzzz-zzzzz-zzzzzzzzzzzzzzz")
322         cr.ContainerRecord.ContainerImage = otherPDH
323
324         err := cr.LoadImage()
325         c.Check(err.Error(), Equals, "First file in the container image collection does not end in .tar")
326 }
327
328 func (s *TestSuite) TestLoadImageKeepReadError(c *C) {
329         // (4) Collection doesn't contain image
330         docker := NewTestDockerClient()
331         cr := NewContainerRunner(&ArvTestClient{}, KeepReadErrorTestClient{}, docker, "zzzzz-zzzzz-zzzzzzzzzzzzzzz")
332         cr.ContainerRecord.ContainerImage = hwPDH
333
334         err := cr.LoadImage()
335         c.Check(err, NotNil)
336 }
337
338 type ClosableBuffer struct {
339         bytes.Buffer
340 }
341
342 type TestLogs struct {
343         Stdout ClosableBuffer
344         Stderr ClosableBuffer
345 }
346
347 func (this *ClosableBuffer) Close() error {
348         return nil
349 }
350
351 func (this *TestLogs) NewTestLoggingWriter(logstr string) io.WriteCloser {
352         if logstr == "stdout" {
353                 return &this.Stdout
354         }
355         if logstr == "stderr" {
356                 return &this.Stderr
357         }
358         return nil
359 }
360
361 func (s *TestSuite) TestRunContainer(c *C) {
362         docker := NewTestDockerClient()
363         docker.fn = func(t *TestDockerClient) {
364                 t.stdoutWriter.Write([]byte("Hello world\n"))
365                 t.stdoutWriter.Close()
366                 t.stderrWriter.Close()
367                 t.finish <- dockerclient.WaitResult{}
368         }
369         cr := NewContainerRunner(&ArvTestClient{}, &KeepTestClient{}, docker, "zzzzz-zzzzz-zzzzzzzzzzzzzzz")
370
371         var logs TestLogs
372         cr.NewLogWriter = logs.NewTestLoggingWriter
373         cr.ContainerRecord.ContainerImage = hwPDH
374         cr.ContainerRecord.Command = []string{"./hw"}
375         err := cr.LoadImage()
376         c.Check(err, IsNil)
377
378         err = cr.StartContainer()
379         c.Check(err, IsNil)
380
381         err = cr.AttachLogs()
382         c.Check(err, IsNil)
383
384         err = cr.WaitFinish()
385         c.Check(err, IsNil)
386
387         c.Check(strings.HasSuffix(logs.Stdout.String(), "Hello world\n"), Equals, true)
388         c.Check(logs.Stderr.String(), Equals, "")
389 }
390
391 func (s *TestSuite) TestCommitLogs(c *C) {
392         api := &ArvTestClient{}
393         kc := &KeepTestClient{}
394         cr := NewContainerRunner(api, kc, nil, "zzzzz-zzzzz-zzzzzzzzzzzzzzz")
395         cr.CrunchLog.Timestamper = (&TestTimestamper{}).Timestamp
396
397         cr.CrunchLog.Print("Hello world!")
398         cr.CrunchLog.Print("Goodbye")
399         cr.finalState = "Complete"
400
401         err := cr.CommitLogs()
402         c.Check(err, IsNil)
403
404         c.Check(api.Content["collection"].(arvadosclient.Dict)["name"], Equals, "logs for zzzzz-zzzzz-zzzzzzzzzzzzzzz")
405         c.Check(api.Content["collection"].(arvadosclient.Dict)["manifest_text"], Equals, ". 744b2e4553123b02fa7b452ec5c18993+123 0:123:crunch-run.txt\n")
406         c.Check(*cr.LogsPDH, Equals, "63da7bdacf08c40f604daad80c261e9a+60")
407 }
408
409 func (s *TestSuite) TestUpdateContainerRecordRunning(c *C) {
410         api := &ArvTestClient{}
411         kc := &KeepTestClient{}
412         cr := NewContainerRunner(api, kc, nil, "zzzzz-zzzzz-zzzzzzzzzzzzzzz")
413
414         err := cr.UpdateContainerRecordRunning()
415         c.Check(err, IsNil)
416
417         c.Check(api.Content["container"].(arvadosclient.Dict)["state"], Equals, "Running")
418 }
419
420 func (s *TestSuite) TestUpdateContainerRecordComplete(c *C) {
421         api := &ArvTestClient{}
422         kc := &KeepTestClient{}
423         cr := NewContainerRunner(api, kc, nil, "zzzzz-zzzzz-zzzzzzzzzzzzzzz")
424
425         cr.LogsPDH = new(string)
426         *cr.LogsPDH = "d3a229d2fe3690c2c3e75a71a153c6a3+60"
427
428         cr.ExitCode = new(int)
429         *cr.ExitCode = 42
430         cr.finalState = "Complete"
431
432         err := cr.UpdateContainerRecordComplete()
433         c.Check(err, IsNil)
434
435         c.Check(api.Content["container"].(arvadosclient.Dict)["log"], Equals, *cr.LogsPDH)
436         c.Check(api.Content["container"].(arvadosclient.Dict)["exit_code"], Equals, *cr.ExitCode)
437         c.Check(api.Content["container"].(arvadosclient.Dict)["state"], Equals, "Complete")
438 }
439
440 func (s *TestSuite) TestUpdateContainerRecordCancelled(c *C) {
441         api := &ArvTestClient{}
442         kc := &KeepTestClient{}
443         cr := NewContainerRunner(api, kc, nil, "zzzzz-zzzzz-zzzzzzzzzzzzzzz")
444         cr.Cancelled = true
445         cr.finalState = "Cancelled"
446
447         err := cr.UpdateContainerRecordComplete()
448         c.Check(err, IsNil)
449
450         c.Check(api.Content["container"].(arvadosclient.Dict)["log"], IsNil)
451         c.Check(api.Content["container"].(arvadosclient.Dict)["exit_code"], IsNil)
452         c.Check(api.Content["container"].(arvadosclient.Dict)["state"], Equals, "Cancelled")
453 }
454
455 // Used by the TestFullRun*() test below to DRY up boilerplate setup to do full
456 // dress rehersal of the Run() function, starting from a JSON container record.
457 func FullRunHelper(c *C, record string, fn func(t *TestDockerClient)) (api *ArvTestClient, cr *ContainerRunner) {
458         rec := ContainerRecord{}
459         err := json.NewDecoder(strings.NewReader(record)).Decode(&rec)
460         c.Check(err, IsNil)
461
462         docker := NewTestDockerClient()
463         docker.fn = fn
464         docker.RemoveImage(hwImageId, true)
465
466         api = &ArvTestClient{ContainerRecord: rec}
467         cr = NewContainerRunner(api, &KeepTestClient{}, docker, "zzzzz-zzzzz-zzzzzzzzzzzzzzz")
468
469         err = cr.Run()
470         c.Check(err, IsNil)
471         c.Check(api.WasSetRunning, Equals, true)
472
473         c.Check(api.Content["container"].(arvadosclient.Dict)["log"], NotNil)
474
475         if err != nil {
476                 for k, v := range api.Logs {
477                         c.Log(k)
478                         c.Log(v.String())
479                 }
480         }
481
482         return
483 }
484
485 func (s *TestSuite) TestFullRunHello(c *C) {
486         api, _ := FullRunHelper(c, `{
487     "command": ["echo", "hello world"],
488     "container_image": "d4ab34d3d4f8a72f5c4973051ae69fab+122",
489     "cwd": ".",
490     "environment": {},
491     "mounts": {"/tmp": {"kind": "tmp"} },
492     "output_path": "/tmp",
493     "priority": 1,
494     "runtime_constraints": {}
495 }`, func(t *TestDockerClient) {
496                 t.stdoutWriter.Write([]byte("hello world\n"))
497                 t.stdoutWriter.Close()
498                 t.stderrWriter.Close()
499                 t.finish <- dockerclient.WaitResult{}
500         })
501
502         c.Check(api.Content["container"].(arvadosclient.Dict)["exit_code"], Equals, 0)
503         c.Check(api.Content["container"].(arvadosclient.Dict)["state"], Equals, "Complete")
504
505         c.Check(strings.HasSuffix(api.Logs["stdout"].String(), "hello world\n"), Equals, true)
506
507 }
508
509 func (s *TestSuite) TestFullRunStderr(c *C) {
510         api, _ := FullRunHelper(c, `{
511     "command": ["/bin/sh", "-c", "echo hello ; echo world 1>&2 ; exit 1"],
512     "container_image": "d4ab34d3d4f8a72f5c4973051ae69fab+122",
513     "cwd": ".",
514     "environment": {},
515     "mounts": {"/tmp": {"kind": "tmp"} },
516     "output_path": "/tmp",
517     "priority": 1,
518     "runtime_constraints": {}
519 }`, func(t *TestDockerClient) {
520                 t.stdoutWriter.Write([]byte("hello\n"))
521                 t.stderrWriter.Write([]byte("world\n"))
522                 t.stdoutWriter.Close()
523                 t.stderrWriter.Close()
524                 t.finish <- dockerclient.WaitResult{ExitCode: 1}
525         })
526
527         c.Check(api.Content["container"].(arvadosclient.Dict)["log"], NotNil)
528         c.Check(api.Content["container"].(arvadosclient.Dict)["exit_code"], Equals, 1)
529         c.Check(api.Content["container"].(arvadosclient.Dict)["state"], Equals, "Complete")
530
531         c.Check(strings.HasSuffix(api.Logs["stdout"].String(), "hello\n"), Equals, true)
532         c.Check(strings.HasSuffix(api.Logs["stderr"].String(), "world\n"), Equals, true)
533 }
534
535 func (s *TestSuite) TestFullRunDefaultCwd(c *C) {
536         api, _ := FullRunHelper(c, `{
537     "command": ["pwd"],
538     "container_image": "d4ab34d3d4f8a72f5c4973051ae69fab+122",
539     "cwd": ".",
540     "environment": {},
541     "mounts": {"/tmp": {"kind": "tmp"} },
542     "output_path": "/tmp",
543     "priority": 1,
544     "runtime_constraints": {}
545 }`, func(t *TestDockerClient) {
546                 t.stdoutWriter.Write([]byte(t.cwd + "\n"))
547                 t.stdoutWriter.Close()
548                 t.stderrWriter.Close()
549                 t.finish <- dockerclient.WaitResult{ExitCode: 0}
550         })
551
552         c.Check(api.Content["container"].(arvadosclient.Dict)["exit_code"], Equals, 0)
553         c.Check(api.Content["container"].(arvadosclient.Dict)["state"], Equals, "Complete")
554
555         c.Check(strings.HasSuffix(api.Logs["stdout"].String(), "/\n"), Equals, true)
556 }
557
558 func (s *TestSuite) TestFullRunSetCwd(c *C) {
559         api, _ := FullRunHelper(c, `{
560     "command": ["pwd"],
561     "container_image": "d4ab34d3d4f8a72f5c4973051ae69fab+122",
562     "cwd": "/bin",
563     "environment": {},
564     "mounts": {"/tmp": {"kind": "tmp"} },
565     "output_path": "/tmp",
566     "priority": 1,
567     "runtime_constraints": {}
568 }`, func(t *TestDockerClient) {
569                 t.stdoutWriter.Write([]byte(t.cwd + "\n"))
570                 t.stdoutWriter.Close()
571                 t.stderrWriter.Close()
572                 t.finish <- dockerclient.WaitResult{ExitCode: 0}
573         })
574
575         c.Check(api.Content["container"].(arvadosclient.Dict)["exit_code"], Equals, 0)
576         c.Check(api.Content["container"].(arvadosclient.Dict)["state"], Equals, "Complete")
577
578         c.Check(strings.HasSuffix(api.Logs["stdout"].String(), "/bin\n"), Equals, true)
579 }
580
581 func (s *TestSuite) TestCancel(c *C) {
582         record := `{
583     "command": ["/bin/sh", "-c", "echo foo && sleep 30 && echo bar"],
584     "container_image": "d4ab34d3d4f8a72f5c4973051ae69fab+122",
585     "cwd": ".",
586     "environment": {},
587     "mounts": {"/tmp": {"kind": "tmp"} },
588     "output_path": "/tmp",
589     "priority": 1,
590     "runtime_constraints": {}
591 }`
592
593         rec := ContainerRecord{}
594         err := json.NewDecoder(strings.NewReader(record)).Decode(&rec)
595         c.Check(err, IsNil)
596
597         docker := NewTestDockerClient()
598         docker.fn = func(t *TestDockerClient) {
599                 <-t.stop
600                 t.stdoutWriter.Write([]byte("foo\n"))
601                 t.stdoutWriter.Close()
602                 t.stderrWriter.Close()
603                 t.finish <- dockerclient.WaitResult{ExitCode: 0}
604         }
605         docker.RemoveImage(hwImageId, true)
606
607         api := &ArvTestClient{ContainerRecord: rec}
608         cr := NewContainerRunner(api, &KeepTestClient{}, docker, "zzzzz-zzzzz-zzzzzzzzzzzzzzz")
609
610         go func() {
611                 for cr.ContainerID == "" {
612                         time.Sleep(1 * time.Second)
613                 }
614                 cr.SigChan <- syscall.SIGINT
615         }()
616
617         err = cr.Run()
618
619         c.Check(err, IsNil)
620
621         c.Check(api.Content["container"].(arvadosclient.Dict)["log"], NotNil)
622
623         if err != nil {
624                 for k, v := range api.Logs {
625                         c.Log(k)
626                         c.Log(v.String())
627                 }
628         }
629
630         c.Check(api.Content["container"].(arvadosclient.Dict)["state"], Equals, "Cancelled")
631
632         c.Check(strings.HasSuffix(api.Logs["stdout"].String(), "foo\n"), Equals, true)
633
634 }
635
636 func (s *TestSuite) TestFullRunSetEnv(c *C) {
637         api, _ := FullRunHelper(c, `{
638     "command": ["/bin/sh", "-c", "echo $FROBIZ"],
639     "container_image": "d4ab34d3d4f8a72f5c4973051ae69fab+122",
640     "cwd": "/bin",
641     "environment": {"FROBIZ": "bilbo"},
642     "mounts": {"/tmp": {"kind": "tmp"} },
643     "output_path": "/tmp",
644     "priority": 1,
645     "runtime_constraints": {}
646 }`, func(t *TestDockerClient) {
647                 t.stdoutWriter.Write([]byte(t.env[0][7:] + "\n"))
648                 t.stdoutWriter.Close()
649                 t.stderrWriter.Close()
650                 t.finish <- dockerclient.WaitResult{ExitCode: 0}
651         })
652
653         c.Check(api.Content["container"].(arvadosclient.Dict)["exit_code"], Equals, 0)
654         c.Check(api.Content["container"].(arvadosclient.Dict)["state"], Equals, "Complete")
655
656         c.Check(strings.HasSuffix(api.Logs["stdout"].String(), "bilbo\n"), Equals, true)
657 }
658
659 type ArvMountCmdLine struct {
660         Cmd []string
661 }
662
663 func (am *ArvMountCmdLine) ArvMountTest(c []string) (*exec.Cmd, error) {
664         am.Cmd = c
665         return nil, nil
666 }
667
668 func (s *TestSuite) TestSetupMounts(c *C) {
669         api := &ArvTestClient{}
670         kc := &KeepTestClient{}
671         cr := NewContainerRunner(api, kc, nil, "zzzzz-zzzzz-zzzzzzzzzzzzzzz")
672         am := &ArvMountCmdLine{}
673         cr.RunArvMount = am.ArvMountTest
674
675         i := 0
676         cr.MkTempDir = func(string, string) (string, error) {
677                 i += 1
678                 return fmt.Sprintf("/tmp/mktmpdir%d", i), nil
679         }
680
681         cr.ContainerRecord.Mounts = make(map[string]Mount)
682         cr.ContainerRecord.Mounts["/tmp"] = Mount{Kind: "tmp"}
683         cr.OutputPath = "/tmp"
684
685         err := cr.SetupMounts()
686         c.Check(err, IsNil)
687         c.Check(am.Cmd, DeepEquals, []string{"--foreground", "--mount-by-pdh", "by_id", "/tmp/mktmpdir1"})
688         c.Check(cr.Binds, DeepEquals, []string{"/tmp/mktmpdir2:/tmp"})
689
690         i = 0
691         cr.ContainerRecord.Mounts = make(map[string]Mount)
692         cr.ContainerRecord.Mounts["/keeptmp"] = Mount{Kind: "collection", Writable: true}
693         cr.OutputPath = "/keeptmp"
694
695         os.MkdirAll("/tmp/mktmpdir1/tmp0", os.ModePerm)
696
697         err = cr.SetupMounts()
698         c.Check(err, IsNil)
699         c.Check(am.Cmd, DeepEquals, []string{"--foreground", "--mount-tmp", "tmp0", "--mount-by-pdh", "by_id", "/tmp/mktmpdir1"})
700         c.Check(cr.Binds, DeepEquals, []string{"/tmp/mktmpdir1/tmp0:/keeptmp"})
701
702         i = 0
703         cr.ContainerRecord.Mounts = make(map[string]Mount)
704         cr.ContainerRecord.Mounts["/keepinp"] = Mount{Kind: "collection", PortableDataHash: "59389a8f9ee9d399be35462a0f92541c+53"}
705         cr.ContainerRecord.Mounts["/keeptmp"] = Mount{Kind: "collection", Writable: true}
706         cr.OutputPath = "/keeptmp"
707
708         os.MkdirAll("/tmp/mktmpdir1/by_id/59389a8f9ee9d399be35462a0f92541c+53", os.ModePerm)
709
710         err = cr.SetupMounts()
711         c.Check(err, IsNil)
712         c.Check(am.Cmd, DeepEquals, []string{"--foreground", "--mount-tmp", "tmp0", "--mount-by-pdh", "by_id", "/tmp/mktmpdir1"})
713         c.Check(cr.Binds, DeepEquals, []string{"/tmp/mktmpdir1/by_id/59389a8f9ee9d399be35462a0f92541c+53:/keepinp:ro", "/tmp/mktmpdir1/tmp0:/keeptmp"})
714 }