12891: Remove superfluous ThinDockerClientProxy wrapper.
[arvados.git] / services / crunch-run / crunchrun_test.go
index 97faa89fb134ae8cb12a1d36ce2ef458da9af020..a524e481d1bb014dbe8cc35fc111a70f4ce4fdd7 100644 (file)
@@ -7,7 +7,6 @@ package main
 import (
        "bufio"
        "bytes"
-       "context"
        "crypto/md5"
        "encoding/json"
        "errors"
@@ -28,7 +27,9 @@ import (
 
        "git.curoverse.com/arvados.git/sdk/go/arvados"
        "git.curoverse.com/arvados.git/sdk/go/arvadosclient"
+       "git.curoverse.com/arvados.git/sdk/go/arvadostest"
        "git.curoverse.com/arvados.git/sdk/go/manifest"
+       "golang.org/x/net/context"
 
        dockertypes "github.com/docker/docker/api/types"
        dockercontainer "github.com/docker/docker/api/types/container"
@@ -130,6 +131,19 @@ func (t *TestDockerClient) ContainerCreate(ctx context.Context, config *dockerco
 }
 
 func (t *TestDockerClient) ContainerStart(ctx context.Context, container string, options dockertypes.ContainerStartOptions) error {
+       if t.finish == 3 {
+               return errors.New(`Error response from daemon: oci runtime error: container_linux.go:247: starting container process caused "process_linux.go:359: container init caused \"rootfs_linux.go:54: mounting \\\"/tmp/keep453790790/by_id/99999999999999999999999999999999+99999/myGenome\\\" to rootfs \\\"/tmp/docker/overlay2/9999999999999999999999999999999999999999999999999999999999999999/merged\\\" at \\\"/tmp/docker/overlay2/9999999999999999999999999999999999999999999999999999999999999999/merged/keep/99999999999999999999999999999999+99999/myGenome\\\" caused \\\"no such file or directory\\\"\""`)
+       }
+       if t.finish == 4 {
+               return errors.New(`panic: standard_init_linux.go:175: exec user process caused "no such file or directory"`)
+       }
+       if t.finish == 5 {
+               return errors.New(`Error response from daemon: Cannot start container 41f26cbc43bcc1280f4323efb1830a394ba8660c9d1c2b564ba42bf7f7694845: [8] System error: no such file or directory`)
+       }
+       if t.finish == 6 {
+               return errors.New(`Error response from daemon: Cannot start container 58099cd76c834f3dc2a4fb76c8028f049ae6d4fdf0ec373e1f2cfea030670c2d: [8] System error: exec: "foobar": executable file not found in $PATH`)
+       }
+
        if container == "abcde" {
                // t.fn gets executed in ContainerWait
                return nil
@@ -230,8 +244,23 @@ func (client *ArvTestClient) Call(method, resourceType, uuid, action string, par
 func (client *ArvTestClient) CallRaw(method, resourceType, uuid, action string,
        parameters arvadosclient.Dict) (reader io.ReadCloser, err error) {
        var j []byte
-       if method == "GET" && resourceType == "containers" && action == "" && !client.callraw {
-               j, err = json.Marshal(client.Container)
+       if method == "GET" && resourceType == "nodes" && uuid == "" && action == "" {
+               j = []byte(`{
+                       "kind": "arvados#nodeList",
+                       "items": [{
+                               "uuid": "zzzzz-7ekkf-2z3mc76g2q73aio",
+                               "hostname": "compute2",
+                               "properties": {"total_cpu_cores": 16}
+                       }]}`)
+       } else if method == "GET" && resourceType == "containers" && action == "" && !client.callraw {
+               if uuid == "" {
+                       j, err = json.Marshal(map[string]interface{}{
+                               "items": []interface{}{client.Container},
+                               "kind":  "arvados#nodeList",
+                       })
+               } else {
+                       j, err = json.Marshal(client.Container)
+               }
        } else {
                j = []byte(`{
                        "command": ["sleep", "1"],
@@ -754,6 +783,7 @@ func (s *TestSuite) TestCrunchstat(c *C) {
 }
 
 func (s *TestSuite) TestNodeInfoLog(c *C) {
+       os.Setenv("SLURMD_NODENAME", "compute2")
        api, _, _ := FullRunHelper(c, `{
                "command": ["sleep", "1"],
                "container_image": "d4ab34d3d4f8a72f5c4973051ae69fab+122",
@@ -772,12 +802,19 @@ func (s *TestSuite) TestNodeInfoLog(c *C) {
        c.Check(api.CalledWith("container.exit_code", 0), NotNil)
        c.Check(api.CalledWith("container.state", "Complete"), NotNil)
 
+       c.Assert(api.Logs["node"], NotNil)
+       json := api.Logs["node"].String()
+       c.Check(json, Matches, `(?ms).*"uuid": *"zzzzz-7ekkf-2z3mc76g2q73aio".*`)
+       c.Check(json, Matches, `(?ms).*"total_cpu_cores": *16.*`)
+       c.Check(json, Not(Matches), `(?ms).*"info":.*`)
+
        c.Assert(api.Logs["node-info"], NotNil)
-       c.Check(api.Logs["node-info"].String(), Matches, `(?ms).*Host Information.*`)
-       c.Check(api.Logs["node-info"].String(), Matches, `(?ms).*CPU Information.*`)
-       c.Check(api.Logs["node-info"].String(), Matches, `(?ms).*Memory Information.*`)
-       c.Check(api.Logs["node-info"].String(), Matches, `(?ms).*Disk Space.*`)
-       c.Check(api.Logs["node-info"].String(), Matches, `(?ms).*Disk INodes.*`)
+       json = api.Logs["node-info"].String()
+       c.Check(json, Matches, `(?ms).*Host Information.*`)
+       c.Check(json, Matches, `(?ms).*CPU Information.*`)
+       c.Check(json, Matches, `(?ms).*Memory Information.*`)
+       c.Check(json, Matches, `(?ms).*Disk Space.*`)
+       c.Check(json, Matches, `(?ms).*Disk INodes.*`)
 }
 
 func (s *TestSuite) TestContainerRecordLog(c *C) {
@@ -937,7 +974,7 @@ func (s *TestSuite) testStopContainer(c *C, setup func(cr *ContainerRunner)) {
 
        c.Check(api.CalledWith("container.log", nil), NotNil)
        c.Check(api.CalledWith("container.state", "Cancelled"), NotNil)
-       c.Check(strings.HasSuffix(api.Logs["stdout"].String(), "foo\n"), Equals, true)
+       c.Check(api.Logs["stdout"].String(), Matches, "(?ms).*foo\n$")
 }
 
 func (s *TestSuite) TestFullRunSetEnv(c *C) {
@@ -1250,6 +1287,64 @@ func (s *TestSuite) TestSetupMounts(c *C) {
                cr.CleanupDirs()
                checkEmpty()
        }
+
+       // git_tree mounts
+       {
+               i = 0
+               cr.ArvMountPoint = ""
+               (*GitMountSuite)(nil).useTestGitServer(c)
+               cr.token = arvadostest.ActiveToken
+               cr.Container.Mounts = make(map[string]arvados.Mount)
+               cr.Container.Mounts = map[string]arvados.Mount{
+                       "/tip": {
+                               Kind:   "git_tree",
+                               UUID:   arvadostest.Repository2UUID,
+                               Commit: "fd3531f42995344f36c30b79f55f27b502f3d344",
+                               Path:   "/",
+                       },
+                       "/non-tip": {
+                               Kind:   "git_tree",
+                               UUID:   arvadostest.Repository2UUID,
+                               Commit: "5ebfab0522851df01fec11ec55a6d0f4877b542e",
+                               Path:   "/",
+                       },
+               }
+               cr.OutputPath = "/tmp"
+
+               err := cr.SetupMounts()
+               c.Check(err, IsNil)
+
+               // dirMap[mountpoint] == tmpdir
+               dirMap := make(map[string]string)
+               for _, bind := range cr.Binds {
+                       tokens := strings.Split(bind, ":")
+                       dirMap[tokens[1]] = tokens[0]
+
+                       if cr.Container.Mounts[tokens[1]].Writable {
+                               c.Check(len(tokens), Equals, 2)
+                       } else {
+                               c.Check(len(tokens), Equals, 3)
+                               c.Check(tokens[2], Equals, "ro")
+                       }
+               }
+
+               data, err := ioutil.ReadFile(dirMap["/tip"] + "/dir1/dir2/file with mode 0644")
+               c.Check(err, IsNil)
+               c.Check(string(data), Equals, "\000\001\002\003")
+               _, err = ioutil.ReadFile(dirMap["/tip"] + "/file only on testbranch")
+               c.Check(err, FitsTypeOf, &os.PathError{})
+               c.Check(os.IsNotExist(err), Equals, true)
+
+               data, err = ioutil.ReadFile(dirMap["/non-tip"] + "/dir1/dir2/file with mode 0644")
+               c.Check(err, IsNil)
+               c.Check(string(data), Equals, "\000\001\002\003")
+               data, err = ioutil.ReadFile(dirMap["/non-tip"] + "/file only on testbranch")
+               c.Check(err, IsNil)
+               c.Check(string(data), Equals, "testfile\n")
+
+               cr.CleanupDirs()
+               checkEmpty()
+       }
 }
 
 func (s *TestSuite) TestStdout(c *C) {
@@ -1835,3 +1930,91 @@ func (s *TestSuite) TestFullBrokenDocker2(c *C) {
        c.Check(api.Logs["crunch-run"].String(), Matches, "(?ms).*unable to run containers.*")
        c.Check(api.Logs["crunch-run"].String(), Matches, "(?ms).*No broken node hook.*")
 }
+
+func (s *TestSuite) TestFullBrokenDocker3(c *C) {
+       ech := ""
+       brokenNodeHook = &ech
+
+       api, _, _ := FullRunHelper(c, `{
+    "command": ["echo", "hello world"],
+    "container_image": "d4ab34d3d4f8a72f5c4973051ae69fab+122",
+    "cwd": ".",
+    "environment": {},
+    "mounts": {"/tmp": {"kind": "tmp"} },
+    "output_path": "/tmp",
+    "priority": 1,
+    "runtime_constraints": {}
+}`, nil, 3, func(t *TestDockerClient) {
+               t.logWriter.Write(dockerLog(1, "hello world\n"))
+               t.logWriter.Close()
+       })
+
+       c.Check(api.CalledWith("container.state", "Cancelled"), NotNil)
+       c.Check(api.Logs["crunch-run"].String(), Matches, "(?ms).*unable to run containers.*")
+}
+
+func (s *TestSuite) TestBadCommand1(c *C) {
+       ech := ""
+       brokenNodeHook = &ech
+
+       api, _, _ := FullRunHelper(c, `{
+    "command": ["echo", "hello world"],
+    "container_image": "d4ab34d3d4f8a72f5c4973051ae69fab+122",
+    "cwd": ".",
+    "environment": {},
+    "mounts": {"/tmp": {"kind": "tmp"} },
+    "output_path": "/tmp",
+    "priority": 1,
+    "runtime_constraints": {}
+}`, nil, 4, func(t *TestDockerClient) {
+               t.logWriter.Write(dockerLog(1, "hello world\n"))
+               t.logWriter.Close()
+       })
+
+       c.Check(api.CalledWith("container.state", "Cancelled"), NotNil)
+       c.Check(api.Logs["crunch-run"].String(), Matches, "(?ms).*Possible causes:.*is missing.*")
+}
+
+func (s *TestSuite) TestBadCommand2(c *C) {
+       ech := ""
+       brokenNodeHook = &ech
+
+       api, _, _ := FullRunHelper(c, `{
+    "command": ["echo", "hello world"],
+    "container_image": "d4ab34d3d4f8a72f5c4973051ae69fab+122",
+    "cwd": ".",
+    "environment": {},
+    "mounts": {"/tmp": {"kind": "tmp"} },
+    "output_path": "/tmp",
+    "priority": 1,
+    "runtime_constraints": {}
+}`, nil, 5, func(t *TestDockerClient) {
+               t.logWriter.Write(dockerLog(1, "hello world\n"))
+               t.logWriter.Close()
+       })
+
+       c.Check(api.CalledWith("container.state", "Cancelled"), NotNil)
+       c.Check(api.Logs["crunch-run"].String(), Matches, "(?ms).*Possible causes:.*is missing.*")
+}
+
+func (s *TestSuite) TestBadCommand3(c *C) {
+       ech := ""
+       brokenNodeHook = &ech
+
+       api, _, _ := FullRunHelper(c, `{
+    "command": ["echo", "hello world"],
+    "container_image": "d4ab34d3d4f8a72f5c4973051ae69fab+122",
+    "cwd": ".",
+    "environment": {},
+    "mounts": {"/tmp": {"kind": "tmp"} },
+    "output_path": "/tmp",
+    "priority": 1,
+    "runtime_constraints": {}
+}`, nil, 6, func(t *TestDockerClient) {
+               t.logWriter.Write(dockerLog(1, "hello world\n"))
+               t.logWriter.Close()
+       })
+
+       c.Check(api.CalledWith("container.state", "Cancelled"), NotNil)
+       c.Check(api.Logs["crunch-run"].String(), Matches, "(?ms).*Possible causes:.*is missing.*")
+}