Skip to content

Commit e28f0d9

Browse files
author
Bryan C. Mills
committed
cmd/go/internal/modfetch: return structured errors from proxy operations
CL 181881 added structured error types for direct fetches. Use those same structured errors to format proxy errors consistently. Also ensure that an empty @v/list is treated as equivalent to the module not existing at all. Updates #27173 Updates #32715 Change-Id: I203fd8259bc4f28b3389745f1a1fde936b0fa24d Reviewed-on: https://go-review.googlesource.com/c/go/+/183619 Run-TryBot: Bryan C. Mills <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Jay Conrod <[email protected]>
1 parent df901bc commit e28f0d9

File tree

6 files changed

+113
-21
lines changed

6 files changed

+113
-21
lines changed

src/cmd/go/internal/modfetch/codehost/codehost.go

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,8 +116,20 @@ type UnknownRevisionError struct {
116116
func (e *UnknownRevisionError) Error() string {
117117
return "unknown revision " + e.Rev
118118
}
119+
func (UnknownRevisionError) Is(err error) bool {
120+
return err == os.ErrNotExist
121+
}
122+
123+
// ErrNoCommits is an error equivalent to os.ErrNotExist indicating that a given
124+
// repository or module contains no commits.
125+
var ErrNoCommits error = noCommitsError{}
119126

120-
func (e *UnknownRevisionError) Is(err error) bool {
127+
type noCommitsError struct{}
128+
129+
func (noCommitsError) Error() string {
130+
return "no commits"
131+
}
132+
func (noCommitsError) Is(err error) bool {
121133
return err == os.ErrNotExist
122134
}
123135

src/cmd/go/internal/modfetch/codehost/git.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,7 @@ func (r *gitRepo) Latest() (*RevInfo, error) {
222222
return nil, r.refsErr
223223
}
224224
if r.refs["HEAD"] == "" {
225-
return nil, fmt.Errorf("no commits")
225+
return nil, ErrNoCommits
226226
}
227227
return r.Stat(r.refs["HEAD"])
228228
}

src/cmd/go/internal/modfetch/proxy.go

Lines changed: 36 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,26 @@ func (p *proxyRepo) ModulePath() string {
196196
return p.path
197197
}
198198

199+
// versionError returns err wrapped in a ModuleError for p.path.
200+
func (p *proxyRepo) versionError(version string, err error) error {
201+
if version != "" && version != module.CanonicalVersion(version) {
202+
return &module.ModuleError{
203+
Path: p.path,
204+
Err: &module.InvalidVersionError{
205+
Version: version,
206+
Pseudo: IsPseudoVersion(version),
207+
Err: err,
208+
},
209+
}
210+
}
211+
212+
return &module.ModuleError{
213+
Path: p.path,
214+
Version: version,
215+
Err: err,
216+
}
217+
}
218+
199219
func (p *proxyRepo) getBytes(path string) ([]byte, error) {
200220
body, err := p.getBody(path)
201221
if err != nil {
@@ -226,7 +246,7 @@ func (p *proxyRepo) getBody(path string) (io.ReadCloser, error) {
226246
func (p *proxyRepo) Versions(prefix string) ([]string, error) {
227247
data, err := p.getBytes("@v/list")
228248
if err != nil {
229-
return nil, err
249+
return nil, p.versionError("", err)
230250
}
231251
var list []string
232252
for _, line := range strings.Split(string(data), "\n") {
@@ -242,7 +262,7 @@ func (p *proxyRepo) Versions(prefix string) ([]string, error) {
242262
func (p *proxyRepo) latest() (*RevInfo, error) {
243263
data, err := p.getBytes("@v/list")
244264
if err != nil {
245-
return nil, err
265+
return nil, p.versionError("", err)
246266
}
247267
var best time.Time
248268
var bestVersion string
@@ -257,7 +277,7 @@ func (p *proxyRepo) latest() (*RevInfo, error) {
257277
}
258278
}
259279
if bestVersion == "" {
260-
return nil, fmt.Errorf("no commits")
280+
return nil, p.versionError("", codehost.ErrNoCommits)
261281
}
262282
info := &RevInfo{
263283
Version: bestVersion,
@@ -271,21 +291,21 @@ func (p *proxyRepo) latest() (*RevInfo, error) {
271291
func (p *proxyRepo) Stat(rev string) (*RevInfo, error) {
272292
encRev, err := module.EncodeVersion(rev)
273293
if err != nil {
274-
return nil, err
294+
return nil, p.versionError(rev, err)
275295
}
276296
data, err := p.getBytes("@v/" + encRev + ".info")
277297
if err != nil {
278-
return nil, err
298+
return nil, p.versionError(rev, err)
279299
}
280300
info := new(RevInfo)
281301
if err := json.Unmarshal(data, info); err != nil {
282-
return nil, err
302+
return nil, p.versionError(rev, err)
283303
}
284304
if info.Version != rev && rev == module.CanonicalVersion(rev) && module.Check(p.path, rev) == nil {
285305
// If we request a correct, appropriate version for the module path, the
286306
// proxy must return either exactly that version or an error — not some
287307
// arbitrary other version.
288-
return nil, fmt.Errorf("requested canonical version %s, but proxy returned info for version %s", rev, info.Version)
308+
return nil, p.versionError(rev, fmt.Errorf("proxy returned info for version %s instead of requested version", info.Version))
289309
}
290310
return info, nil
291311
}
@@ -298,48 +318,48 @@ func (p *proxyRepo) Latest() (*RevInfo, error) {
298318
}
299319
info := new(RevInfo)
300320
if err := json.Unmarshal(data, info); err != nil {
301-
return nil, err
321+
return nil, p.versionError("", err)
302322
}
303323
return info, nil
304324
}
305325

306326
func (p *proxyRepo) GoMod(version string) ([]byte, error) {
307327
if version != module.CanonicalVersion(version) {
308-
return nil, fmt.Errorf("version %s is not canonical", version)
328+
return nil, p.versionError(version, fmt.Errorf("internal error: version passed to GoMod is not canonical"))
309329
}
310330

311331
encVer, err := module.EncodeVersion(version)
312332
if err != nil {
313-
return nil, err
333+
return nil, p.versionError(version, err)
314334
}
315335
data, err := p.getBytes("@v/" + encVer + ".mod")
316336
if err != nil {
317-
return nil, err
337+
return nil, p.versionError(version, err)
318338
}
319339
return data, nil
320340
}
321341

322342
func (p *proxyRepo) Zip(dst io.Writer, version string) error {
323343
if version != module.CanonicalVersion(version) {
324-
return fmt.Errorf("version %s is not canonical", version)
344+
return p.versionError(version, fmt.Errorf("internal error: version passed to Zip is not canonical"))
325345
}
326346

327347
encVer, err := module.EncodeVersion(version)
328348
if err != nil {
329-
return err
349+
return p.versionError(version, err)
330350
}
331351
body, err := p.getBody("@v/" + encVer + ".zip")
332352
if err != nil {
333-
return err
353+
return p.versionError(version, err)
334354
}
335355
defer body.Close()
336356

337357
lr := &io.LimitedReader{R: body, N: codehost.MaxZipFile + 1}
338358
if _, err := io.Copy(dst, lr); err != nil {
339-
return err
359+
return p.versionError(version, err)
340360
}
341361
if lr.N <= 0 {
342-
return fmt.Errorf("downloaded zip file too large")
362+
return p.versionError(version, fmt.Errorf("downloaded zip file too large"))
343363
}
344364
return nil
345365
}

src/cmd/go/internal/modload/query.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -243,8 +243,13 @@ func queryProxy(proxy, path, query, current string, allowed func(module.Version)
243243
if mayUseLatest {
244244
// Special case for "latest": if no tags match, use latest commit in repo,
245245
// provided it is not excluded.
246-
if latest, err := repo.Latest(); err == nil && allowed(module.Version{Path: path, Version: latest.Version}) {
247-
return lookup(latest.Version)
246+
latest, err := repo.Latest()
247+
if err == nil {
248+
if allowed(module.Version{Path: path, Version: latest.Version}) {
249+
return lookup(latest.Version)
250+
}
251+
} else if !errors.Is(err, os.ErrNotExist) {
252+
return nil, err
248253
}
249254
}
250255

src/cmd/go/internal/module/module.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -536,7 +536,10 @@ func EncodePath(path string) (encoding string, err error) {
536536
// and not contain exclamation marks.
537537
func EncodeVersion(v string) (encoding string, err error) {
538538
if err := checkElem(v, true); err != nil || strings.Contains(v, "!") {
539-
return "", fmt.Errorf("disallowed version string %q", v)
539+
return "", &InvalidVersionError{
540+
Version: v,
541+
Err: fmt.Errorf("disallowed version string"),
542+
}
540543
}
541544
return encodeString(v)
542545
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
env GO111MODULE=on
2+
env GOSUMDB=off
3+
4+
go mod download example.com/[email protected]
5+
6+
# If the proxy serves a bogus result for the @latest version,
7+
# reading that version should cause 'go get' to fail.
8+
env GOPROXY=file:///$WORK/badproxy
9+
cp go.mod.orig go.mod
10+
! go get -d example.com/join/subpkg
11+
stderr 'go get example.com/join/subpkg: example.com/join/[email protected]: .*'
12+
13+
# If @v/list is empty, the 'go' command should still try to resolve
14+
# other module paths.
15+
env GOPROXY=file:///$WORK/emptysub
16+
cp go.mod.orig go.mod
17+
go get -d example.com/join/subpkg
18+
go list -m example.com/join/...
19+
! stdout 'example.com/join/subpkg'
20+
stdout 'example.com/join v1.1.0'
21+
22+
# If @v/list includes a version that the proxy does not actually serve,
23+
# that version is treated as nonexistent.
24+
env GOPROXY=file:///$WORK/notfound
25+
cp go.mod.orig go.mod
26+
go get -d example.com/join/subpkg
27+
go list -m example.com/join/...
28+
! stdout 'example.com/join/subpkg'
29+
stdout 'example.com/join v1.1.0'
30+
31+
-- go.mod.orig --
32+
module example.com/othermodule
33+
go 1.13
34+
-- $WORK/badproxy/example.com/join/subpkg/@v/list --
35+
v0.0.0-20190624000000-123456abcdef
36+
-- $WORK/badproxy/example.com/join/subpkg/@v/v0.0.0-20190624000000-123456abcdef.info --
37+
This file is not valid JSON.
38+
-- $WORK/badproxy/example.com/join/@v/list --
39+
v1.1.0
40+
-- $WORK/badproxy/example.com/join/@v/v1.1.0.info --
41+
{"Version": "v1.1.0"}
42+
-- $WORK/emptysub/example.com/join/subpkg/@v/list --
43+
-- $WORK/emptysub/example.com/join/@v/list --
44+
v1.1.0
45+
-- $WORK/emptysub/example.com/join/@v/v1.1.0.info --
46+
{"Version": "v1.1.0"}
47+
-- $WORK/notfound/example.com/join/subpkg/@v/list --
48+
v1.0.0-does-not-exist
49+
-- $WORK/notfound/example.com/join/@v/list --
50+
v1.1.0
51+
-- $WORK/notfound/example.com/join/@v/v1.1.0.info --
52+
{"Version": "v1.1.0"}

0 commit comments

Comments
 (0)