Merge branch '18870-installer' refs #18870
[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         "strings"
10 )
11
12 func (fs *customFileSystem) defaultUUID(uuid string) (string, error) {
13         if uuid != "" {
14                 return uuid, nil
15         }
16         var resp User
17         err := fs.RequestAndDecode(&resp, "GET", "arvados/v1/users/current", nil, nil)
18         if err != nil {
19                 return "", err
20         }
21         return resp.UUID, nil
22 }
23
24 // loadOneChild loads only the named child, if it exists.
25 func (fs *customFileSystem) projectsLoadOne(parent inode, uuid, name string) (inode, error) {
26         uuid, err := fs.defaultUUID(uuid)
27         if err != nil {
28                 return nil, err
29         }
30
31         var contents CollectionList
32         for _, subst := range []string{"/", fs.forwardSlashNameSubstitution} {
33                 contents = CollectionList{}
34                 err = fs.RequestAndDecode(&contents, "GET", "arvados/v1/groups/"+uuid+"/contents", nil, ResourceListParams{
35                         Count: "none",
36                         Filters: []Filter{
37                                 {"name", "=", strings.Replace(name, subst, "/", -1)},
38                                 {"uuid", "is_a", []string{"arvados#collection", "arvados#group"}},
39                                 {"groups.group_class", "=", "project"},
40                         },
41                 })
42                 if err != nil {
43                         return nil, err
44                 }
45                 if len(contents.Items) > 0 || fs.forwardSlashNameSubstitution == "/" || fs.forwardSlashNameSubstitution == "" || !strings.Contains(name, fs.forwardSlashNameSubstitution) {
46                         break
47                 }
48                 // If the requested name contains the configured "/"
49                 // replacement string and didn't match a
50                 // project/collection exactly, we'll try again with
51                 // "/" in its place, so a lookup of a munged name
52                 // works regardless of whether the directory listing
53                 // has been populated with escaped names.
54                 //
55                 // Note this doesn't handle items whose names contain
56                 // both "/" and the substitution string.
57         }
58         if len(contents.Items) == 0 {
59                 return nil, nil
60         }
61         coll := contents.Items[0]
62
63         if strings.Contains(coll.UUID, "-j7d0g-") {
64                 // Group item was loaded into a Collection var -- but
65                 // we only need the Name and UUID anyway, so it's OK.
66                 return fs.newProjectNode(parent, coll.Name, coll.UUID), nil
67         } else if strings.Contains(coll.UUID, "-4zz18-") {
68                 return deferredCollectionFS(fs, parent, coll), nil
69         } else {
70                 log.Printf("group contents: unrecognized UUID in response: %q", coll.UUID)
71                 return nil, ErrInvalidArgument
72         }
73 }
74
75 func (fs *customFileSystem) projectsLoadAll(parent inode, uuid string) ([]inode, error) {
76         uuid, err := fs.defaultUUID(uuid)
77         if err != nil {
78                 return nil, err
79         }
80
81         var inodes []inode
82
83         // When #17424 is resolved, remove the outer loop here and use
84         // []string{"arvados#collection", "arvados#group"} directly as the uuid
85         // filter.
86         for _, class := range []string{"arvados#collection", "arvados#group"} {
87                 // Note: the "filters" slice's backing array might be reused
88                 // by append(filters,...) below. This isn't goroutine safe,
89                 // but all accesses are in the same goroutine, so it's OK.
90                 filters := []Filter{
91                         {"uuid", "is_a", class},
92                 }
93                 if class == "arvados#group" {
94                         filters = append(filters, Filter{"group_class", "=", "project"})
95                 }
96
97                 params := ResourceListParams{
98                         Count:   "none",
99                         Filters: filters,
100                         Order:   "uuid",
101                 }
102
103                 for {
104                         // The groups content endpoint returns Collection and Group (project)
105                         // objects. This function only accesses the UUID and Name field. Both
106                         // collections and groups have those fields, so it is easier to just treat
107                         // the ObjectList that comes back as a CollectionList.
108                         var resp CollectionList
109                         err = fs.RequestAndDecode(&resp, "GET", "arvados/v1/groups/"+uuid+"/contents", nil, params)
110                         if err != nil {
111                                 return nil, err
112                         }
113                         if len(resp.Items) == 0 {
114                                 break
115                         }
116                         for _, i := range resp.Items {
117                                 if fs.forwardSlashNameSubstitution != "" {
118                                         i.Name = strings.Replace(i.Name, "/", fs.forwardSlashNameSubstitution, -1)
119                                 }
120                                 if !permittedName(i.Name) {
121                                         continue
122                                 }
123                                 if strings.Contains(i.UUID, "-j7d0g-") {
124                                         inodes = append(inodes, fs.newProjectNode(parent, i.Name, i.UUID))
125                                 } else if strings.Contains(i.UUID, "-4zz18-") {
126                                         inodes = append(inodes, deferredCollectionFS(fs, parent, i))
127                                 } else {
128                                         log.Printf("group contents: unrecognized UUID in response: %q", i.UUID)
129                                         return nil, ErrInvalidArgument
130                                 }
131                         }
132                         params.Filters = append(filters, Filter{"uuid", ">", resp.Items[len(resp.Items)-1].UUID})
133                 }
134         }
135         return inodes, nil
136 }