1 // Copyright (C) The Arvados Authors. All rights reserved.
3 // SPDX-License-Identifier: AGPL-3.0
17 "git.arvados.org/arvados.git/lib/config"
18 "git.arvados.org/arvados.git/sdk/go/arvados"
19 "git.arvados.org/arvados.git/sdk/go/arvadosclient"
20 "git.arvados.org/arvados.git/sdk/go/arvadostest"
21 "git.arvados.org/arvados.git/sdk/go/ctxlog"
22 "git.arvados.org/arvados.git/sdk/go/keepclient"
26 var _ = Suite(&integrationSuite{})
28 type integrationSuite struct {
30 image arvados.Collection
31 input arvados.Collection
35 cr arvados.ContainerRequest
36 client *arvados.Client
37 ac *arvadosclient.ArvadosClient
38 kc *keepclient.KeepClient
40 logCollection arvados.Collection
41 outputCollection arvados.Collection
44 func (s *integrationSuite) SetUpSuite(c *C) {
45 _, err := exec.LookPath("docker")
47 c.Skip("looks like docker is not installed")
50 arvadostest.StartKeep(2, true)
52 out, err := exec.Command("docker", "load", "--input", busyboxDockerImage(c)).CombinedOutput()
55 out, err = exec.Command("arv-keepdocker", "--no-resume", "busybox:uclibc").Output()
56 imageUUID := strings.TrimSpace(string(out))
57 c.Logf("image uuid %s", imageUUID)
58 if !c.Check(err, IsNil) {
59 if err, ok := err.(*exec.ExitError); ok {
60 c.Logf("%s", err.Stderr)
64 err = arvados.NewClientFromEnv().RequestAndDecode(&s.image, "GET", "arvados/v1/collections/"+imageUUID, nil, nil)
66 c.Logf("image pdh %s", s.image.PortableDataHash)
68 s.client = arvados.NewClientFromEnv()
69 s.ac, err = arvadosclient.New(s.client)
71 s.kc = keepclient.New(s.ac)
72 fs, err := s.input.FileSystem(s.client, s.kc)
74 f, err := fs.OpenFile("inputfile", os.O_CREATE|os.O_WRONLY, 0755)
76 _, err = f.Write([]byte("inputdata"))
80 s.input.ManifestText, err = fs.MarshalManifest(".")
82 err = s.client.RequestAndDecode(&s.input, "POST", "arvados/v1/collections", nil, map[string]interface{}{
83 "ensure_unique_name": true,
84 "collection": map[string]interface{}{
85 "manifest_text": s.input.ManifestText,
89 c.Logf("input pdh %s", s.input.PortableDataHash)
91 s.logCollection = arvados.Collection{}
92 s.outputCollection = arvados.Collection{}
95 func (s *integrationSuite) TearDownSuite(c *C) {
100 err := s.client.RequestAndDecode(nil, "POST", "database/reset", nil, nil)
104 func (s *integrationSuite) SetUpTest(c *C) {
106 s.stdin = bytes.Buffer{}
107 s.stdout = bytes.Buffer{}
108 s.stderr = bytes.Buffer{}
109 s.cr = arvados.ContainerRequest{
112 OutputPath: "/mnt/out",
113 ContainerImage: s.image.PortableDataHash,
114 Mounts: map[string]arvados.Mount{
117 Content: []interface{}{
119 map[string]string{"foo": "bar"},
125 PortableDataHash: s.input.PortableDataHash,
132 RuntimeConstraints: arvados.RuntimeConstraints{
140 func (s *integrationSuite) setup(c *C) {
141 err := s.client.RequestAndDecode(&s.cr, "POST", "arvados/v1/container_requests", nil, map[string]interface{}{"container_request": map[string]interface{}{
142 "priority": s.cr.Priority,
144 "command": s.cr.Command,
145 "output_path": s.cr.OutputPath,
146 "container_image": s.cr.ContainerImage,
147 "mounts": s.cr.Mounts,
148 "runtime_constraints": s.cr.RuntimeConstraints,
149 "use_existing": false,
152 c.Assert(s.cr.ContainerUUID, Not(Equals), "")
153 err = s.client.RequestAndDecode(nil, "POST", "arvados/v1/containers/"+s.cr.ContainerUUID+"/lock", nil, nil)
157 func (s *integrationSuite) TestRunTrivialContainerWithDocker(c *C) {
159 s.testRunTrivialContainer(c)
162 func (s *integrationSuite) TestRunTrivialContainerWithSingularity(c *C) {
163 s.engine = "singularity"
164 s.testRunTrivialContainer(c)
167 func (s *integrationSuite) TestRunTrivialContainerWithLocalKeepstore(c *C) {
168 cfg, err := config.NewLoader(nil, ctxlog.TestLogger(c)).Load()
170 cluster, err := cfg.GetCluster("")
172 for uuid, volume := range cluster.Volumes {
173 volume.AccessViaHosts = nil
174 volume.Replication = 2
175 cluster.Volumes[uuid] = volume
179 err = json.NewEncoder(&s.stdin).Encode(ConfigData{
187 s.testRunTrivialContainer(c)
189 fs, err := s.logCollection.FileSystem(s.client, s.kc)
191 f, err := fs.Open("keepstore.txt")
193 buf, err := ioutil.ReadAll(f)
195 c.Check(string(buf), Matches, `(?ms).*"reqMethod":"GET".*`)
196 c.Check(string(buf), Matches, `(?ms).*"reqMethod":"PUT".*,"reqPath":"0e3bcff26d51c895a60ea0d4585e134d".*`)
199 func (s *integrationSuite) testRunTrivialContainer(c *C) {
200 if err := exec.Command("which", s.engine).Run(); err != nil {
201 c.Skip(fmt.Sprintf("%s: %s", s.engine, err))
203 s.cr.Command = []string{"sh", "-c", "cat /mnt/in/inputfile >/mnt/out/inputfile && cat /mnt/json >/mnt/out/json && ! touch /mnt/in/shouldbereadonly && mkdir /mnt/out/emptydir"}
207 "-runtime-engine=" + s.engine,
208 "-enable-memory-limit=false",
211 if s.stdin.Len() > 0 {
212 args = append([]string{"-stdin-config=true"}, args...)
214 code := command{}.RunCommand("crunch-run", args, &s.stdin, io.MultiWriter(&s.stdout, os.Stderr), io.MultiWriter(&s.stderr, os.Stderr))
215 c.Logf("\n===== stdout =====\n%s", s.stdout.String())
216 c.Logf("\n===== stderr =====\n%s", s.stderr.String())
217 c.Check(code, Equals, 0)
218 err := s.client.RequestAndDecode(&s.cr, "GET", "arvados/v1/container_requests/"+s.cr.UUID, nil, nil)
220 c.Logf("Finished container request: %#v", s.cr)
222 var log arvados.Collection
223 err = s.client.RequestAndDecode(&log, "GET", "arvados/v1/collections/"+s.cr.LogUUID, nil, nil)
225 fs, err := log.FileSystem(s.client, s.kc)
227 if d, err := fs.Open("/"); c.Check(err, IsNil) {
228 fis, err := d.Readdir(-1)
230 for _, fi := range fis {
234 f, err := fs.Open(fi.Name())
236 buf, err := ioutil.ReadAll(f)
238 c.Logf("\n===== %s =====\n%s", fi.Name(), buf)
241 s.logCollection = log
243 var output arvados.Collection
244 err = s.client.RequestAndDecode(&output, "GET", "arvados/v1/collections/"+s.cr.OutputUUID, nil, nil)
246 fs, err = output.FileSystem(s.client, s.kc)
248 if f, err := fs.Open("inputfile"); c.Check(err, IsNil) {
250 buf, err := ioutil.ReadAll(f)
252 c.Check(string(buf), Equals, "inputdata")
254 if f, err := fs.Open("json"); c.Check(err, IsNil) {
256 buf, err := ioutil.ReadAll(f)
258 c.Check(string(buf), Equals, `["foo",{"foo":"bar"},null]`)
260 if fi, err := fs.Stat("emptydir"); c.Check(err, IsNil) {
261 c.Check(fi.IsDir(), Equals, true)
263 if d, err := fs.Open("emptydir"); c.Check(err, IsNil) {
265 fis, err := d.Readdir(-1)
267 // crunch-run still saves a ".keep" file to preserve
268 // empty dirs even though that shouldn't be
269 // necessary. Ideally we would do:
270 // c.Check(fis, HasLen, 0)
271 for _, fi := range fis {
272 c.Check(fi.Name(), Equals, ".keep")
275 s.outputCollection = output