17755: Merge branch 'main' into 17755-add-singularity-to-compute-image
[arvados.git] / lib / recovercollection / cmd_test.go
1 // Copyright (C) The Arvados Authors. All rights reserved.
2 //
3 // SPDX-License-Identifier: AGPL-3.0
4
5 package recovercollection
6
7 import (
8         "bytes"
9         "encoding/json"
10         "io/ioutil"
11         "os"
12         "testing"
13         "time"
14
15         "git.arvados.org/arvados.git/lib/config"
16         "git.arvados.org/arvados.git/sdk/go/arvadostest"
17         "git.arvados.org/arvados.git/sdk/go/ctxlog"
18         "gopkg.in/check.v1"
19 )
20
21 func Test(t *testing.T) {
22         check.TestingT(t)
23 }
24
25 var _ = check.Suite(&Suite{})
26
27 type Suite struct{}
28
29 func (*Suite) SetUpSuite(c *check.C) {
30         arvadostest.StartAPI()
31         arvadostest.StartKeep(2, true)
32 }
33
34 func (*Suite) TestUnrecoverableBlock(c *check.C) {
35         tmp := c.MkDir()
36         mfile := tmp + "/manifest"
37         ioutil.WriteFile(mfile, []byte(". aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+410 0:410:Gone\n"), 0777)
38         var stdout, stderr bytes.Buffer
39         exitcode := Command.RunCommand("recovercollection.test", []string{"-log-level=debug", mfile}, &bytes.Buffer{}, &stdout, &stderr)
40         c.Check(exitcode, check.Equals, 1)
41         c.Check(stdout.String(), check.Equals, "")
42         c.Log(stderr.String())
43         c.Check(stderr.String(), check.Matches, `(?ms).*msg="not found" block=aaaaa.*`)
44         c.Check(stderr.String(), check.Matches, `(?ms).*msg="untrash failed" block=aaaaa.*`)
45         c.Check(stderr.String(), check.Matches, `(?ms).*msg=unrecoverable block=aaaaa.*`)
46         c.Check(stderr.String(), check.Matches, `(?ms).*msg="recovery failed".*`)
47 }
48
49 func (*Suite) TestUntrashAndTouchBlock(c *check.C) {
50         tmp := c.MkDir()
51         mfile := tmp + "/manifest"
52         ioutil.WriteFile(mfile, []byte(". dcd0348cb2532ee90c99f1b846efaee7+13 0:13:test.txt\n"), 0777)
53
54         logger := ctxlog.TestLogger(c)
55         loader := config.NewLoader(&bytes.Buffer{}, logger)
56         cfg, err := loader.Load()
57         c.Assert(err, check.IsNil)
58         cluster, err := cfg.GetCluster("")
59         c.Assert(err, check.IsNil)
60         var datadirs []string
61         for _, v := range cluster.Volumes {
62                 var params struct {
63                         Root string
64                 }
65                 err := json.Unmarshal(v.DriverParameters, &params)
66                 c.Assert(err, check.IsNil)
67                 if params.Root != "" {
68                         datadirs = append(datadirs, params.Root)
69                         err := os.Remove(params.Root + "/dcd/dcd0348cb2532ee90c99f1b846efaee7")
70                         if err != nil && !os.IsNotExist(err) {
71                                 c.Error(err)
72                         }
73                 }
74         }
75         c.Logf("keepstore datadirs are %q", datadirs)
76
77         // Currently StartKeep(2, true) uses dirs called "keep0" and
78         // "keep1" so we could just put our fake trashed file in keep0
79         // ... but we don't want to rely on arvadostest's
80         // implementation details, so we put a trashed file in every
81         // dir that keepstore might be using.
82         for _, datadir := range datadirs {
83                 if fi, err := os.Stat(datadir); err != nil || !fi.IsDir() {
84                         continue
85                 }
86                 c.Logf("placing backdated trashed block in datadir %q", datadir)
87                 trashfile := datadir + "/dcd/dcd0348cb2532ee90c99f1b846efaee7.trash.999999999"
88                 os.Mkdir(datadir+"/dcd", 0777)
89                 err = ioutil.WriteFile(trashfile, []byte("undelete test"), 0777)
90                 c.Assert(err, check.IsNil)
91                 t := time.Now().Add(-time.Hour * 24 * 365)
92                 err = os.Chtimes(trashfile, t, t)
93                 c.Assert(err, check.IsNil)
94         }
95
96         var stdout, stderr bytes.Buffer
97         exitcode := Command.RunCommand("recovercollection.test", []string{"-log-level=debug", mfile}, &bytes.Buffer{}, &stdout, &stderr)
98         c.Check(exitcode, check.Equals, 0)
99         c.Check(stdout.String(), check.Matches, `zzzzz-4zz18-.{15}\n`)
100         c.Log(stderr.String())
101         c.Check(stderr.String(), check.Matches, `(?ms).*msg=untrashed block=dcd0348.*`)
102         c.Check(stderr.String(), check.Matches, `(?ms).*msg="updated timestamp" block=dcd0348.*`)
103
104         found := false
105         for _, datadir := range datadirs {
106                 buf, err := ioutil.ReadFile(datadir + "/dcd/dcd0348cb2532ee90c99f1b846efaee7")
107                 if err == nil {
108                         found = true
109                         c.Check(buf, check.DeepEquals, []byte("undelete test"))
110                         fi, err := os.Stat(datadir + "/dcd/dcd0348cb2532ee90c99f1b846efaee7")
111                         if c.Check(err, check.IsNil) {
112                                 c.Logf("recovered block's modtime is %s", fi.ModTime())
113                                 c.Check(time.Now().Sub(fi.ModTime()) < time.Hour, check.Equals, true)
114                         }
115                 }
116         }
117         c.Check(found, check.Equals, true)
118 }
119
120 func (*Suite) TestUnusableManifestSourceArg(c *check.C) {
121         for _, trial := range []struct {
122                 srcArg    string
123                 errRegexp string
124         }{
125                 {"zzzzz-4zz18-aaaaaaaaaaaaaaa", `(?ms).*msg="log entry not found".*`},
126                 {"zzzzz-57u5n-aaaaaaaaaaaaaaa", `(?ms).*msg="log entry not found.*`},
127                 {"zzzzz-57u5n-containerlog006", `(?ms).*msg="log entry properties\.old_attributes\.manifest_text missing or empty".*`},
128                 {"zzzzz-j7d0g-aaaaaaaaaaaaaaa", `(?ms).*msg="looks like a UUID but not a log or collection UUID.*`},
129         } {
130                 var stdout, stderr bytes.Buffer
131                 exitcode := Command.RunCommand("recovercollection.test", []string{"-log-level=debug", trial.srcArg}, &bytes.Buffer{}, &stdout, &stderr)
132                 c.Check(exitcode, check.Equals, 1)
133                 c.Check(stdout.String(), check.Equals, "")
134                 c.Log(stderr.String())
135                 c.Check(stderr.String(), check.Matches, trial.errRegexp)
136         }
137 }