14328: Check ContainerInspect instead of List, give up on error.
[arvados.git] / services / crunch-run / crunchrun_test.go
index 8ee462586d4b994743b141c712f445ee02268253..eb4f220e227d399b31b0669b15ba77c75449e557 100644 (file)
@@ -47,6 +47,7 @@ var _ = Suite(&TestSuite{})
 type TestSuite struct {
        client *arvados.Client
        docker *TestDockerClient
+       runner *ContainerRunner
 }
 
 func (s *TestSuite) SetUpTest(c *C) {
@@ -103,6 +104,7 @@ type TestDockerClient struct {
        api         *ArvTestClient
        realTemp    string
        calledWait  bool
+       ctrExited   bool
 }
 
 func NewTestDockerClient() *TestDockerClient {
@@ -176,6 +178,17 @@ func (t *TestDockerClient) ContainerWait(ctx context.Context, container string,
        return body, err
 }
 
+func (t *TestDockerClient) ContainerInspect(ctx context.Context, id string) (c dockertypes.ContainerJSON, err error) {
+       c.ContainerJSONBase = &dockertypes.ContainerJSONBase{}
+       c.ID = "abcde"
+       if t.ctrExited {
+               c.State = &dockertypes.ContainerState{Status: "exited", Dead: true}
+       } else {
+               c.State = &dockertypes.ContainerState{Status: "running", Pid: 1234, Running: true}
+       }
+       return
+}
+
 func (t *TestDockerClient) ImageInspectWithRaw(ctx context.Context, image string) (dockertypes.ImageInspect, []byte, error) {
        if t.exitCode == 2 {
                return dockertypes.ImageInspect{}, nil, fmt.Errorf("Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?")
@@ -230,6 +243,7 @@ func (client *ArvTestClient) Create(resourceType string,
                mt := parameters["collection"].(arvadosclient.Dict)["manifest_text"].(string)
                outmap := output.(*arvados.Collection)
                outmap.PortableDataHash = fmt.Sprintf("%x+%d", md5.Sum([]byte(mt)), len(mt))
+               outmap.UUID = fmt.Sprintf("zzzzz-4zz18-%15.15x", md5.Sum([]byte(mt)))
        }
 
        return nil
@@ -316,6 +330,10 @@ func (client *ArvTestClient) Update(resourceType string, uuid string, parameters
                if parameters["container"].(arvadosclient.Dict)["state"] == "Running" {
                        client.WasSetRunning = true
                }
+       } else if resourceType == "collections" {
+               mt := parameters["collection"].(arvadosclient.Dict)["manifest_text"].(string)
+               output.(*arvados.Collection).UUID = uuid
+               output.(*arvados.Collection).PortableDataHash = fmt.Sprintf("%x", md5.Sum([]byte(mt)))
        }
        return nil
 }
@@ -402,6 +420,10 @@ func (fw FileWrapper) Write([]byte) (int, error) {
        return 0, errors.New("not implemented")
 }
 
+func (fw FileWrapper) Sync() error {
+       return errors.New("not implemented")
+}
+
 func (client *KeepTestClient) ManifestFileReader(m manifest.Manifest, filename string) (arvados.File, error) {
        if filename == hwImageId+".tar" {
                rdr := ioutil.NopCloser(&bytes.Buffer{})
@@ -727,7 +749,9 @@ func (s *TestSuite) fullRunHelper(c *C, record string, extraMounts []string, exi
        defer kc.Close()
        cr, err = NewContainerRunner(s.client, api, kc, s.docker, "zzzzz-zzzzz-zzzzzzzzzzzzzzz")
        c.Assert(err, IsNil)
+       s.runner = cr
        cr.statInterval = 100 * time.Millisecond
+       cr.containerWatchdogInterval = time.Second
        am := &ArvMountCmdLine{}
        cr.RunArvMount = am.ArvMountTest
 
@@ -789,7 +813,7 @@ func (s *TestSuite) TestFullRunHello(c *C) {
     "mounts": {"/tmp": {"kind": "tmp"} },
     "output_path": "/tmp",
     "priority": 1,
-    "runtime_constraints": {}
+       "runtime_constraints": {}
 }`, nil, 0, func(t *TestDockerClient) {
                t.logWriter.Write(dockerLog(1, "hello world\n"))
                t.logWriter.Close()
@@ -801,6 +825,44 @@ func (s *TestSuite) TestFullRunHello(c *C) {
 
 }
 
+func (s *TestSuite) TestRunTimeExceeded(c *C) {
+       api, _, _ := s.fullRunHelper(c, `{
+    "command": ["sleep", "3"],
+    "container_image": "d4ab34d3d4f8a72f5c4973051ae69fab+122",
+    "cwd": ".",
+    "environment": {},
+    "mounts": {"/tmp": {"kind": "tmp"} },
+    "output_path": "/tmp",
+    "priority": 1,
+       "runtime_constraints": {},
+       "scheduling_parameters":{"max_run_time": 1}
+}`, nil, 0, func(t *TestDockerClient) {
+               time.Sleep(3 * time.Second)
+               t.logWriter.Close()
+       })
+
+       c.Check(api.CalledWith("container.state", "Cancelled"), NotNil)
+       c.Check(api.Logs["crunch-run"].String(), Matches, "(?ms).*maximum run time exceeded.*")
+}
+
+func (s *TestSuite) TestContainerWaitFails(c *C) {
+       api, _, _ := s.fullRunHelper(c, `{
+    "command": ["sleep", "3"],
+    "container_image": "d4ab34d3d4f8a72f5c4973051ae69fab+122",
+    "cwd": ".",
+    "mounts": {"/tmp": {"kind": "tmp"} },
+    "output_path": "/tmp",
+    "priority": 1
+}`, nil, 0, func(t *TestDockerClient) {
+               t.ctrExited = true
+               time.Sleep(10 * time.Second)
+               t.logWriter.Close()
+       })
+
+       c.Check(api.CalledWith("container.state", "Cancelled"), NotNil)
+       c.Check(api.Logs["crunch-run"].String(), Matches, "(?ms).*Container is not running.*")
+}
+
 func (s *TestSuite) TestCrunchstat(c *C) {
        api, _, _ := s.fullRunHelper(c, `{
                "command": ["sleep", "1"],
@@ -1118,7 +1180,7 @@ func (s *TestSuite) TestSetupMounts(c *C) {
                cr.ArvMountPoint = ""
                cr.Container.Mounts = make(map[string]arvados.Mount)
                cr.Container.Mounts["/tmp"] = arvados.Mount{Kind: "tmp"}
-               cr.OutputPath = "/tmp"
+               cr.Container.OutputPath = "/tmp"
                cr.statInterval = 5 * time.Second
                err := cr.SetupMounts()
                c.Check(err, IsNil)
@@ -1137,7 +1199,7 @@ func (s *TestSuite) TestSetupMounts(c *C) {
                cr.Container.Mounts = make(map[string]arvados.Mount)
                cr.Container.Mounts["/out"] = arvados.Mount{Kind: "tmp"}
                cr.Container.Mounts["/tmp"] = arvados.Mount{Kind: "tmp"}
-               cr.OutputPath = "/out"
+               cr.Container.OutputPath = "/out"
 
                err := cr.SetupMounts()
                c.Check(err, IsNil)
@@ -1155,7 +1217,7 @@ func (s *TestSuite) TestSetupMounts(c *C) {
                cr.ArvMountPoint = ""
                cr.Container.Mounts = make(map[string]arvados.Mount)
                cr.Container.Mounts["/tmp"] = arvados.Mount{Kind: "tmp"}
-               cr.OutputPath = "/tmp"
+               cr.Container.OutputPath = "/tmp"
 
                apiflag := true
                cr.Container.RuntimeConstraints.API = &apiflag
@@ -1179,7 +1241,7 @@ func (s *TestSuite) TestSetupMounts(c *C) {
                cr.Container.Mounts = map[string]arvados.Mount{
                        "/keeptmp": {Kind: "collection", Writable: true},
                }
-               cr.OutputPath = "/keeptmp"
+               cr.Container.OutputPath = "/keeptmp"
 
                os.MkdirAll(realTemp+"/keep1/tmp0", os.ModePerm)
 
@@ -1201,7 +1263,7 @@ func (s *TestSuite) TestSetupMounts(c *C) {
                        "/keepinp": {Kind: "collection", PortableDataHash: "59389a8f9ee9d399be35462a0f92541c+53"},
                        "/keepout": {Kind: "collection", Writable: true},
                }
-               cr.OutputPath = "/keepout"
+               cr.Container.OutputPath = "/keepout"
 
                os.MkdirAll(realTemp+"/keep1/by_id/59389a8f9ee9d399be35462a0f92541c+53", os.ModePerm)
                os.MkdirAll(realTemp+"/keep1/tmp0", os.ModePerm)
@@ -1227,7 +1289,7 @@ func (s *TestSuite) TestSetupMounts(c *C) {
                        "/keepinp": {Kind: "collection", PortableDataHash: "59389a8f9ee9d399be35462a0f92541c+53"},
                        "/keepout": {Kind: "collection", Writable: true},
                }
-               cr.OutputPath = "/keepout"
+               cr.Container.OutputPath = "/keepout"
 
                os.MkdirAll(realTemp+"/keep1/by_id/59389a8f9ee9d399be35462a0f92541c+53", os.ModePerm)
                os.MkdirAll(realTemp+"/keep1/tmp0", os.ModePerm)
@@ -1308,7 +1370,7 @@ func (s *TestSuite) TestSetupMounts(c *C) {
                        "/tmp":     {Kind: "tmp"},
                        "/tmp/foo": {Kind: "collection"},
                }
-               cr.OutputPath = "/tmp"
+               cr.Container.OutputPath = "/tmp"
 
                os.MkdirAll(realTemp+"/keep1/tmp0", os.ModePerm)
 
@@ -1338,7 +1400,7 @@ func (s *TestSuite) TestSetupMounts(c *C) {
                                Path:             "baz",
                                Writable:         true},
                }
-               cr.OutputPath = "/tmp"
+               cr.Container.OutputPath = "/tmp"
 
                os.MkdirAll(realTemp+"/keep1/by_id/59389a8f9ee9d399be35462a0f92541c+53", os.ModePerm)
                os.MkdirAll(realTemp+"/keep1/by_id/59389a8f9ee9d399be35462a0f92541d+53/baz", os.ModePerm)
@@ -1367,7 +1429,7 @@ func (s *TestSuite) TestSetupMounts(c *C) {
                        "/tmp":     {Kind: "tmp"},
                        "/tmp/foo": {Kind: "tmp"},
                }
-               cr.OutputPath = "/tmp"
+               cr.Container.OutputPath = "/tmp"
 
                err := cr.SetupMounts()
                c.Check(err, NotNil)
@@ -1415,7 +1477,7 @@ func (s *TestSuite) TestSetupMounts(c *C) {
                                Path:   "/",
                        },
                }
-               cr.OutputPath = "/tmp"
+               cr.Container.OutputPath = "/tmp"
 
                err := cr.SetupMounts()
                c.Check(err, IsNil)
@@ -2043,3 +2105,49 @@ func (s *TestSuite) TestSecretTextMountPoint(c *C) {
        c.Check(api.CalledWith("collection.manifest_text", ". 34819d7beeabb9260a5c854bc85b3e44+10 0:10:secret.conf\n"), IsNil)
        c.Check(api.CalledWith("collection.manifest_text", ""), NotNil)
 }
+
+type FakeProcess struct {
+       cmdLine []string
+}
+
+func (fp FakeProcess) CmdlineSlice() ([]string, error) {
+       return fp.cmdLine, nil
+}
+
+func (s *TestSuite) helpCheckContainerd(c *C, lp func() ([]PsProcess, error)) error {
+       kc := &KeepTestClient{}
+       defer kc.Close()
+       cr, err := NewContainerRunner(s.client, &ArvTestClient{callraw: true}, kc, s.docker, "zzzzz-zzzzz-zzzzzzzzzzzzzzz")
+       cr.checkContainerd = time.Duration(100 * time.Millisecond)
+       c.Assert(err, IsNil)
+       cr.ListProcesses = lp
+
+       s.docker.fn = func(t *TestDockerClient) {
+               time.Sleep(1 * time.Second)
+               t.logWriter.Close()
+       }
+
+       err = cr.CreateContainer()
+       c.Check(err, IsNil)
+
+       err = cr.StartContainer()
+       c.Check(err, IsNil)
+
+       err = cr.WaitFinish()
+       return err
+
+}
+
+func (s *TestSuite) TestCheckContainerdPresent(c *C) {
+       err := s.helpCheckContainerd(c, func() ([]PsProcess, error) {
+               return []PsProcess{FakeProcess{[]string{"docker-containerd"}}}, nil
+       })
+       c.Check(err, IsNil)
+}
+
+func (s *TestSuite) TestCheckContainerdMissing(c *C) {
+       err := s.helpCheckContainerd(c, func() ([]PsProcess, error) {
+               return []PsProcess{FakeProcess{[]string{"abc"}}}, nil
+       })
+       c.Check(err, ErrorMatches, `'containerd' not found in process list.`)
+}