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