1 // Copyright (C) The Arvados Authors. All rights reserved.
3 // SPDX-License-Identifier: Apache-2.0
16 check "gopkg.in/check.v1"
19 type spiedRequest struct {
22 params map[string]interface{}
25 type spyingClient struct {
30 func (sc *spyingClient) RequestAndDecode(dst interface{}, method, path string, body io.Reader, params interface{}) error {
31 var paramsCopy map[string]interface{}
33 json.NewEncoder(&buf).Encode(params)
34 json.NewDecoder(&buf).Decode(¶msCopy)
35 sc.calls = append(sc.calls, spiedRequest{
40 return sc.Client.RequestAndDecode(dst, method, path, body, params)
43 func (s *SiteFSSuite) TestFilterGroup(c *check.C) {
44 // Make sure that a collection and group that match the filter are present,
45 // and that a group that does not match the filter is not present.
46 s.fs.MountProject("fg", fixtureThisFilterGroupUUID)
48 _, err := s.fs.OpenFile("/fg/baz_file", 0, 0)
49 c.Assert(err, check.IsNil)
51 _, err = s.fs.OpenFile("/fg/A Subproject", 0, 0)
52 c.Assert(err, check.IsNil)
54 _, err = s.fs.OpenFile("/fg/A Project", 0, 0)
55 c.Assert(err, check.Not(check.IsNil))
57 // An empty filter means everything that is visible should be returned.
58 s.fs.MountProject("fg2", fixtureAFilterGroupTwoUUID)
60 _, err = s.fs.OpenFile("/fg2/baz_file", 0, 0)
61 c.Assert(err, check.IsNil)
63 _, err = s.fs.OpenFile("/fg2/A Subproject", 0, 0)
64 c.Assert(err, check.IsNil)
66 _, err = s.fs.OpenFile("/fg2/A Project", 0, 0)
67 c.Assert(err, check.IsNil)
69 // An 'is_a' 'arvados#collection' filter means only collections should be returned.
70 s.fs.MountProject("fg3", fixtureAFilterGroupThreeUUID)
72 _, err = s.fs.OpenFile("/fg3/baz_file", 0, 0)
73 c.Assert(err, check.IsNil)
75 _, err = s.fs.OpenFile("/fg3/A Subproject", 0, 0)
76 c.Assert(err, check.Not(check.IsNil))
78 // An 'exists' 'arvados#collection' filter means only collections with certain properties should be returned.
79 s.fs.MountProject("fg4", fixtureAFilterGroupFourUUID)
81 _, err = s.fs.Stat("/fg4/collection with list property with odd values")
82 c.Assert(err, check.IsNil)
84 _, err = s.fs.Stat("/fg4/collection with list property with even values")
85 c.Assert(err, check.IsNil)
87 // A 'contains' 'arvados#collection' filter means only collections with certain properties should be returned.
88 s.fs.MountProject("fg5", fixtureAFilterGroupFiveUUID)
90 _, err = s.fs.Stat("/fg5/collection with list property with odd values")
91 c.Assert(err, check.IsNil)
93 _, err = s.fs.Stat("/fg5/collection with list property with string value")
94 c.Assert(err, check.IsNil)
96 _, err = s.fs.Stat("/fg5/collection with prop2 5")
97 c.Assert(err, check.Not(check.IsNil))
99 _, err = s.fs.Stat("/fg5/collection with list property with even values")
100 c.Assert(err, check.Not(check.IsNil))
103 func (s *SiteFSSuite) TestCurrentUserHome(c *check.C) {
104 s.fs.MountProject("home", "")
105 s.testHomeProject(c, "/home")
108 func (s *SiteFSSuite) TestUsersDir(c *check.C) {
109 s.testHomeProject(c, "/users/active")
112 func (s *SiteFSSuite) testHomeProject(c *check.C, path string) {
113 f, err := s.fs.Open(path)
114 c.Assert(err, check.IsNil)
115 fis, err := f.Readdir(-1)
116 c.Assert(err, check.IsNil)
117 c.Check(len(fis), check.Not(check.Equals), 0)
120 for _, fi := range fis {
121 c.Check(fi.Name(), check.Not(check.Equals), "")
122 if fi.Name() == "A Project" {
126 c.Check(ok, check.Equals, true)
128 f, err = s.fs.Open(path + "/A Project/..")
129 c.Assert(err, check.IsNil)
131 c.Assert(err, check.IsNil)
132 c.Check(fi.IsDir(), check.Equals, true)
133 _, basename := filepath.Split(path)
134 c.Check(fi.Name(), check.Equals, basename)
136 f, err = s.fs.Open(path + "/A Project/A Subproject")
137 c.Assert(err, check.IsNil)
139 c.Assert(err, check.IsNil)
140 c.Check(fi.IsDir(), check.Equals, true)
142 for _, nx := range []string{
143 path + "/Unrestricted public data",
144 path + "/Unrestricted public data/does not exist",
145 path + "/A Project/does not exist",
148 f, err = s.fs.Open(nx)
149 c.Check(err, check.NotNil)
150 c.Check(os.IsNotExist(err), check.Equals, true)
154 func (s *SiteFSSuite) TestProjectReaddirAfterLoadOne(c *check.C) {
155 f, err := s.fs.Open("/users/active/A Project/A Subproject")
156 c.Assert(err, check.IsNil)
158 f, err = s.fs.Open("/users/active/A Project/Project does not exist")
159 c.Assert(err, check.NotNil)
160 f, err = s.fs.Open("/users/active/A Project/A Subproject")
161 c.Assert(err, check.IsNil)
163 f, err = s.fs.Open("/users/active/A Project")
164 c.Assert(err, check.IsNil)
166 fis, err := f.Readdir(-1)
167 c.Assert(err, check.IsNil)
169 var foundSubproject, foundCollection bool
170 for _, fi := range fis {
173 foundSubproject = true
174 case "collection_to_move_around":
175 foundCollection = true
178 c.Check(foundSubproject, check.Equals, true)
179 c.Check(foundCollection, check.Equals, true)
182 func (s *SiteFSSuite) TestSlashInName(c *check.C) {
183 var badCollection Collection
184 err := s.client.RequestAndDecode(&badCollection, "POST", "arvados/v1/collections", nil, map[string]interface{}{
185 "collection": map[string]string{
186 "name": "bad/collection",
187 "owner_uuid": fixtureAProjectUUID,
190 c.Assert(err, check.IsNil)
191 defer s.client.RequestAndDecode(nil, "DELETE", "arvados/v1/collections/"+badCollection.UUID, nil, nil)
194 err = s.client.RequestAndDecode(&badProject, "POST", "arvados/v1/groups", nil, map[string]interface{}{
195 "group": map[string]string{
196 "name": "bad/project",
197 "group_class": "project",
198 "owner_uuid": fixtureAProjectUUID,
201 c.Assert(err, check.IsNil)
202 defer s.client.RequestAndDecode(nil, "DELETE", "arvados/v1/groups/"+badProject.UUID, nil, nil)
204 dir, err := s.fs.Open("/users/active/A Project")
205 c.Assert(err, check.IsNil)
206 fis, err := dir.Readdir(-1)
207 c.Check(err, check.IsNil)
208 for _, fi := range fis {
209 c.Logf("fi.Name() == %q", fi.Name())
210 c.Check(strings.Contains(fi.Name(), "/"), check.Equals, false)
213 // Make a new fs (otherwise content will still be cached from
214 // above) and enable "/" replacement string.
215 s.fs = s.client.SiteFileSystem(s.kc)
216 s.fs.ForwardSlashNameSubstitution("___")
217 dir, err = s.fs.Open("/users/active/A Project/bad___collection")
218 if c.Check(err, check.IsNil) {
219 _, err = dir.Readdir(-1)
220 c.Check(err, check.IsNil)
222 dir, err = s.fs.Open("/users/active/A Project/bad___project")
223 if c.Check(err, check.IsNil) {
224 _, err = dir.Readdir(-1)
225 c.Check(err, check.IsNil)
229 func (s *SiteFSSuite) TestProjectUpdatedByOther(c *check.C) {
230 s.fs.MountProject("home", "")
232 project, err := s.fs.OpenFile("/home/A Project", 0, 0)
233 c.Assert(err, check.IsNil)
235 _, err = s.fs.Open("/home/A Project/oob")
236 c.Check(err, check.NotNil)
239 err = s.client.RequestAndDecode(&oob, "POST", "arvados/v1/collections", nil, map[string]interface{}{
240 "collection": map[string]string{
242 "owner_uuid": fixtureAProjectUUID,
245 c.Assert(err, check.IsNil)
246 defer s.client.RequestAndDecode(nil, "DELETE", "arvados/v1/collections/"+oob.UUID, nil, nil)
249 c.Check(err, check.IsNil)
250 f, err := s.fs.Open("/home/A Project/oob")
251 c.Assert(err, check.IsNil)
253 c.Assert(err, check.IsNil)
254 c.Check(fi.IsDir(), check.Equals, true)
257 wf, err := s.fs.OpenFile("/home/A Project/oob/test.txt", os.O_CREATE|os.O_RDWR, 0700)
258 c.Assert(err, check.IsNil)
259 _, err = wf.Write([]byte("hello oob\n"))
260 c.Check(err, check.IsNil)
262 c.Check(err, check.IsNil)
265 c.Check(err, check.IsNil)
266 _, err = s.fs.Open("/home/A Project/oob/test.txt")
267 c.Check(err, check.IsNil)
269 // Sync again to mark the project dir as stale, so the
270 // collection gets reloaded from the controller on next
273 c.Check(err, check.IsNil)
275 // Ensure collection was flushed by Sync
276 var latest Collection
277 err = s.client.RequestAndDecode(&latest, "GET", "arvados/v1/collections/"+oob.UUID, nil, nil)
278 c.Check(err, check.IsNil)
279 c.Check(latest.ManifestText, check.Matches, `.*:test.txt.*\n`)
281 // Delete test.txt behind s.fs's back by updating the
282 // collection record with an empty ManifestText.
283 err = s.client.RequestAndDecode(nil, "PATCH", "arvados/v1/collections/"+oob.UUID, nil, map[string]interface{}{
284 "collection": map[string]string{
286 "portable_data_hash": "d41d8cd98f00b204e9800998ecf8427e+0",
289 c.Assert(err, check.IsNil)
291 _, err = s.fs.Open("/home/A Project/oob/test.txt")
292 c.Check(err, check.NotNil)
293 _, err = s.fs.Open("/home/A Project/oob")
294 c.Check(err, check.IsNil)
296 err = s.client.RequestAndDecode(nil, "DELETE", "arvados/v1/collections/"+oob.UUID, nil, nil)
297 c.Assert(err, check.IsNil)
299 wf, err = s.fs.OpenFile("/home/A Project/oob/test.txt", os.O_CREATE|os.O_RDWR, 0700)
300 c.Assert(err, check.IsNil)
302 c.Check(err, check.IsNil)
305 c.Check(err, check.NotNil) // can't update the deleted collection
306 _, err = s.fs.Open("/home/A Project/oob")
307 c.Check(err, check.IsNil) // parent dir still has old collection -- didn't reload, because Sync failed
310 func (s *SiteFSSuite) TestProjectUnsupportedOperations(c *check.C) {
311 s.fs.MountByID("by_id")
312 s.fs.MountProject("home", "")
314 _, err := s.fs.OpenFile("/home/A Project/newfilename", os.O_CREATE|os.O_RDWR, 0)
315 c.Check(err, ErrorIs, ErrInvalidOperation)
317 err = s.fs.Mkdir("/home/A Project/newdirname", 0)
318 c.Check(err, ErrorIs, ErrInvalidOperation)
320 err = s.fs.Mkdir("/by_id/newdirname", 0)
321 c.Check(err, ErrorIs, ErrInvalidOperation)
323 err = s.fs.Mkdir("/by_id/"+fixtureAProjectUUID+"/newdirname", 0)
324 c.Check(err, ErrorIs, ErrInvalidOperation)
326 _, err = s.fs.OpenFile("/home/A Project", 0, 0)
327 c.Check(err, check.IsNil)
330 type errorIsChecker struct {
334 var ErrorIs check.Checker = errorIsChecker{
335 &check.CheckerInfo{Name: "ErrorIs", Params: []string{"value", "target"}},
338 func (checker errorIsChecker) Check(params []interface{}, names []string) (result bool, errStr string) {
339 err, ok := params[0].(error)
343 target, ok := params[1].(error)
347 return errors.Is(err, target), ""