13111: Add projects hierarchy under "home".
[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         "log"
9         "os"
10         "sync"
11 )
12
13 // projectnode exposes an Arvados project as a filesystem directory.
14 type projectnode struct {
15         inode
16         uuid      string
17         setupOnce sync.Once
18         err       error
19 }
20
21 func (pn *projectnode) setup() {
22         fs := pn.FS().(*siteFileSystem)
23         if pn.uuid == "" {
24                 var resp User
25                 pn.err = fs.RequestAndDecode(&resp, "GET", "arvados/v1/users/current", nil, nil)
26                 if pn.err != nil {
27                         return
28                 }
29                 pn.uuid = resp.UUID
30         }
31         filters := []Filter{{"owner_uuid", "=", pn.uuid}}
32         params := ResourceListParams{
33                 Filters: filters,
34                 Order:   "uuid",
35         }
36         for {
37                 var resp CollectionList
38                 pn.err = fs.RequestAndDecode(&resp, "GET", "arvados/v1/collections", nil, params)
39                 if pn.err != nil {
40                         // TODO: retry on next access, instead of returning the same error forever
41                         return
42                 }
43                 if len(resp.Items) == 0 {
44                         break
45                 }
46                 for _, i := range resp.Items {
47                         coll := i
48                         if coll.Name == "" {
49                                 continue
50                         }
51                         pn.inode.Child(coll.Name, func(inode) inode {
52                                 return deferredCollectionFS(fs, pn, coll)
53                         })
54                 }
55                 params.Filters = append(filters, Filter{"uuid", ">", resp.Items[len(resp.Items)-1].UUID})
56         }
57
58         filters = append(filters, Filter{"group_class", "=", "project"})
59         params.Filters = filters
60         for {
61                 var resp GroupList
62                 pn.err = fs.RequestAndDecode(&resp, "GET", "arvados/v1/groups", nil, params)
63                 if pn.err != nil {
64                         // TODO: retry on next access, instead of returning the same error forever
65                         return
66                 }
67                 if len(resp.Items) == 0 {
68                         break
69                 }
70                 for _, group := range resp.Items {
71                         if group.Name == "" || group.Name == "." || group.Name == ".." {
72                                 continue
73                         }
74                         pn.inode.Child(group.Name, func(inode) inode {
75                                 return fs.newProjectNode(pn, group.Name, group.UUID)
76                         })
77                 }
78                 params.Filters = append(filters, Filter{"uuid", ">", resp.Items[len(resp.Items)-1].UUID})
79         }
80 }
81
82 func (pn *projectnode) Readdir() []os.FileInfo {
83         pn.setupOnce.Do(pn.setup)
84         return pn.inode.Readdir()
85 }
86
87 func (pn *projectnode) Child(name string, replace func(inode) inode) inode {
88         pn.setupOnce.Do(pn.setup)
89         if pn.err != nil {
90                 log.Printf("BUG: not propagating error setting up %T %v: %s", pn, pn, pn.err)
91                 // TODO: propagate error, instead of just being empty
92                 return nil
93         }
94         if replace == nil {
95                 // lookup
96                 return pn.inode.Child(name, nil)
97         }
98         return pn.inode.Child(name, func(existing inode) inode {
99                 if repl := replace(existing); repl == nil {
100                         // delete
101                         // (TODO)
102                         return pn.Child(name, nil) // not implemented
103                 } else if repl.FileInfo().IsDir() {
104                         // mkdir
105                         // TODO: repl.SetParent(pn, name), etc.
106                         return pn.Child(name, nil) // not implemented
107                 } else {
108                         // create file
109                         // TODO: repl.SetParent(pn, name), etc.
110                         return pn.Child(name, nil) // not implemented
111                 }
112         })
113 }