/
parse_uri.go
235 lines (218 loc) · 6.69 KB
/
parse_uri.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
// Package cli provides easy-to-use commands to manage, monitor, and utilize AIS clusters.
/*
* Copyright (c) 2018-2024, NVIDIA CORPORATION. All rights reserved.
*/
package cli
import (
"errors"
"fmt"
"strings"
"github.com/NVIDIA/aistore/api/apc"
"github.com/NVIDIA/aistore/cmn"
"github.com/NVIDIA/aistore/cmn/cos"
"github.com/urfave/cli"
)
func errBucketNameInvalid(c *cli.Context, arg string, err error) error {
if errV := errArgIsFlag(c, arg); errV != nil {
return errV
}
if strings.Contains(err.Error(), cos.OnlyPlus) && strings.Contains(err.Error(), "bucket name") {
if strings.Contains(arg, ":/") && !strings.Contains(arg, apc.BckProviderSeparator) {
a := strings.Replace(arg, ":/", apc.BckProviderSeparator, 1)
return fmt.Errorf("bucket name in %q is invalid: (did you mean %q?)", arg, a)
}
return fmt.Errorf("bucket name in %q is invalid: "+cos.OnlyPlus, arg)
}
return nil
}
// Return `bckFrom` and `bckTo` - the [shift] and the [shift+1] arguments, respectively
func parseBcks(c *cli.Context, bckFromArg, bckToArg string, shift int, optionalSrcObjname bool) (bckFrom, bckTo cmn.Bck, objFrom string,
err error) {
if c.NArg() == shift {
err = missingArgumentsError(c, bckFromArg, bckToArg)
return
}
if c.NArg() == shift+1 {
err = missingArgumentsError(c, bckToArg)
return
}
// src
var uri string
if optionalSrcObjname {
uri = c.Args().Get(shift)
bckFrom, objFrom, err = parseBckObjURI(c, uri, true /*emptyObjnameOK*/)
} else {
uri = c.Args().Get(shift)
bckFrom, err = parseBckURI(c, uri, true /*error only*/)
}
if err != nil {
if errV := errBucketNameInvalid(c, uri, err); errV != nil {
err = errV
} else {
err = incorrectUsageMsg(c, "invalid %s argument '%s' - %v", bckFromArg, c.Args().Get(shift), err)
}
return
}
// dst
uri = c.Args().Get(shift + 1)
bckTo, err = parseBckURI(c, uri, true)
if err != nil {
if errV := errBucketNameInvalid(c, uri, err); errV != nil {
err = errV
} else {
err = incorrectUsageMsg(c, "invalid %s argument '%s' - %v", bckToArg, c.Args().Get(shift+1), err)
}
}
return
}
func parseBckURI(c *cli.Context, uri string, errorOnly bool) (cmn.Bck, error) {
const validNames = ": ais://mmm, s3://nnn or aws://nnn, gs://ppp or gcp://ppp"
if isWebURL(uri) {
bck := parseURLtoBck(uri)
return bck, nil
}
opts := cmn.ParseURIOpts{}
if !providerRequired {
opts.DefaultProvider = cfg.DefaultProvider
}
bck, objName, err := cmn.ParseBckObjectURI(uri, opts)
switch {
case err != nil:
if errV := errBucketNameInvalid(c, uri, err); errV != nil {
err = errV
}
return cmn.Bck{}, err
case objName != "":
if errorOnly {
return cmn.Bck{}, fmt.Errorf("unexpected object name argument %q", objName)
}
return cmn.Bck{}, objectNameArgNotExpected(c, objName)
case bck.Name == "":
if errorOnly {
return cmn.Bck{}, fmt.Errorf("missing bucket name: %q", uri)
}
return cmn.Bck{}, incorrectUsageMsg(c, "missing bucket name in %q", uri)
default:
if err = bck.Validate(); err != nil {
if errorOnly {
return cmn.Bck{}, err
}
msg := "E.g. " + bucketArgument + validNames
return cmn.Bck{}, cannotExecuteError(c, err, msg)
}
}
return bck, nil
}
// `ais ls` and friends: allow for `provider:` shortcut
func preparseBckObjURI(uri string) string {
if uri == "" {
return uri
}
p := strings.TrimSuffix(uri, ":")
if _, err := cmn.NormalizeProvider(p); err == nil {
return p + apc.BckProviderSeparator
}
return uri // unchanged
}
func parseDest(c *cli.Context, uri string) (bck cmn.Bck, pathSuffix string, err error) {
bck, pathSuffix, err = parseBckObjURI(c, uri, true /*optional objName*/)
if err != nil {
return
} else if bck.IsHTTP() {
err = errors.New("http bucket is not supported as destination")
return
}
pathSuffix = strings.Trim(pathSuffix, "/")
return
}
func parseQueryBckURI(c *cli.Context, uri string) (cmn.QueryBcks, error) {
uri = preparseBckObjURI(uri)
if isWebURL(uri) {
bck := parseURLtoBck(uri)
return cmn.QueryBcks(bck), nil
}
bck, objName, err := cmn.ParseBckObjectURI(uri, cmn.ParseURIOpts{IsQuery: true})
if err != nil {
return cmn.QueryBcks(bck), err
} else if objName != "" {
return cmn.QueryBcks(bck), objectNameArgNotExpected(c, objName)
}
return cmn.QueryBcks(bck), nil
}
func parseBckObjURI(c *cli.Context, uri string, emptyObjnameOK bool) (bck cmn.Bck, objName string, err error) {
if isWebURL(uri) {
var hbo *cmn.HTTPBckObj
hbo, err = cmn.NewHTTPObjPath(uri)
if err != nil {
return
}
bck, objName = hbo.Bck, hbo.ObjName
} else {
var opts cmn.ParseURIOpts
if !providerRequired {
opts.DefaultProvider = cfg.DefaultProvider
}
bck, objName, err = cmn.ParseBckObjectURI(uri, opts)
if err != nil {
if errV := errBucketNameInvalid(c, uri, err); errV != nil {
return bck, objName, errV
}
var msg string
if emptyObjnameOK {
msg = "Expecting " + optionalObjectsArgument + ", e.g.: ais://mmm, s3://nnn/obj2, gs://ppp/a/b/c, etc."
} else {
msg = "Expecting " + objectArgument + ", e.g.: ais://mmm/obj1, s3://nnn/obj2, gs://ppp/obj3, etc."
}
return bck, objName, cannotExecuteError(c, err, msg)
}
}
if bck.Name == "" {
err = incorrectUsageMsg(c, "%q: missing bucket name", uri)
} else if err = bck.Validate(); err != nil {
if errV := errBucketNameInvalid(c, uri, err); errV != nil {
err = errV
} else {
err = cannotExecuteError(c, err, "")
}
} else if objName == "" && !emptyObjnameOK {
err = incorrectUsageMsg(c, "%q: missing object name", uri)
}
return bck, objName, err
}
func parseObjListTemplate(c *cli.Context, objNameOrTmpl string) (objName, listObjs, tmplObjs string, err error) {
var prefix string
if flagIsSet(c, listFlag) {
listObjs = parseStrFlag(c, listFlag)
}
if flagIsSet(c, templateFlag) {
tmplObjs = parseStrFlag(c, templateFlag)
}
// when template is a "pure" prefix (use '--prefix' to disambiguate vs. objName)
if flagIsSet(c, verbObjPrefixFlag) {
prefix = parseStrFlag(c, verbObjPrefixFlag)
if tmplObjs != "" {
err = incorrectUsageMsg(c, errFmtExclusive, qflprn(verbObjPrefixFlag), qflprn(templateFlag))
return "", "", "", err
}
tmplObjs = prefix
}
if listObjs != "" && tmplObjs != "" {
err = incorrectUsageMsg(c, errFmtExclusive, qflprn(listFlag), qflprn(templateFlag))
return "", "", "", err
}
if objNameOrTmpl != "" {
if listObjs != "" || tmplObjs != "" {
what := "object name or prefix"
if isPattern(objNameOrTmpl) {
what = "pattern or template"
}
err = fmt.Errorf("%s (%s) cannot be used together with flags %s and %s (tip: use either one or the other)",
what, objNameOrTmpl, qflprn(listFlag), qflprn(templateFlag))
} else if isPattern(objNameOrTmpl) {
tmplObjs = objNameOrTmpl
} else {
objName = objNameOrTmpl
}
}
return objName, listObjs, tmplObjs, err
}