13111: Reload project dir if fsync(2) was called since last load.
[arvados.git] / sdk / go / arvados / fs_project.go
1 // Copyright (C) The Arvados Authors. All rights reserved.
2 //
3 // SPDX-License-Identifier: Apache-2.0
4
5 package arvados
6
7 import (
8         "os"
9         "sync"
10         "time"
11 )
12
13 // projectnode exposes an Arvados project as a filesystem directory.
14 type projectnode struct {
15         inode
16         uuid string
17         err  error
18
19         loadLock  sync.Mutex
20         loadStart time.Time
21 }
22
23 func (pn *projectnode) load() {
24         fs := pn.FS().(*siteFileSystem)
25
26         pn.loadLock.Lock()
27         defer pn.loadLock.Unlock()
28         if !fs.Stale(pn.loadStart) {
29                 return
30         }
31         pn.loadStart = time.Now()
32
33         if pn.uuid == "" {
34                 var resp User
35                 pn.err = fs.RequestAndDecode(&resp, "GET", "arvados/v1/users/current", nil, nil)
36                 if pn.err != nil {
37                         return
38                 }
39                 pn.uuid = resp.UUID
40         }
41         filters := []Filter{{"owner_uuid", "=", pn.uuid}}
42         params := ResourceListParams{
43                 Filters: filters,
44                 Order:   "uuid",
45         }
46         for {
47                 var resp CollectionList
48                 pn.err = fs.RequestAndDecode(&resp, "GET", "arvados/v1/collections", nil, params)
49                 if pn.err != nil {
50                         return
51                 }
52                 if len(resp.Items) == 0 {
53                         break
54                 }
55                 for _, i := range resp.Items {
56                         coll := i
57                         if coll.Name == "" {
58                                 continue
59                         }
60                         pn.inode.Child(coll.Name, func(inode) (inode, error) {
61                                 return deferredCollectionFS(fs, pn, coll), nil
62                         })
63                 }
64                 params.Filters = append(filters, Filter{"uuid", ">", resp.Items[len(resp.Items)-1].UUID})
65         }
66
67         filters = append(filters, Filter{"group_class", "=", "project"})
68         params.Filters = filters
69         for {
70                 var resp GroupList
71                 pn.err = fs.RequestAndDecode(&resp, "GET", "arvados/v1/groups", nil, params)
72                 if pn.err != nil {
73                         return
74                 }
75                 if len(resp.Items) == 0 {
76                         break
77                 }
78                 for _, group := range resp.Items {
79                         if group.Name == "" || group.Name == "." || group.Name == ".." {
80                                 continue
81                         }
82                         pn.inode.Child(group.Name, func(inode) (inode, error) {
83                                 return fs.newProjectNode(pn, group.Name, group.UUID), nil
84                         })
85                 }
86                 params.Filters = append(filters, Filter{"uuid", ">", resp.Items[len(resp.Items)-1].UUID})
87         }
88         pn.err = nil
89 }
90
91 func (pn *projectnode) Readdir() ([]os.FileInfo, error) {
92         pn.load()
93         if pn.err != nil {
94                 return nil, pn.err
95         }
96         return pn.inode.Readdir()
97 }
98
99 func (pn *projectnode) Child(name string, replace func(inode) (inode, error)) (inode, error) {
100         pn.load()
101         if pn.err != nil {
102                 return nil, pn.err
103         }
104         if replace == nil {
105                 // lookup
106                 return pn.inode.Child(name, nil)
107         }
108         return pn.inode.Child(name, func(existing inode) (inode, error) {
109                 if repl, err := replace(existing); err != nil {
110                         return existing, err
111                 } else if repl == nil {
112                         if existing == nil {
113                                 return nil, nil
114                         }
115                         // rmdir
116                         // (TODO)
117                         return existing, ErrInvalidArgument
118                 } else if existing != nil {
119                         // clobber
120                         return existing, ErrInvalidArgument
121                 } else if repl.FileInfo().IsDir() {
122                         // mkdir
123                         // TODO: repl.SetParent(pn, name), etc.
124                         return existing, ErrInvalidArgument
125                 } else {
126                         // create file
127                         return existing, ErrInvalidArgument
128                 }
129         })
130 }