Skip to content

Commit bf75699

Browse files
committed
mod/modregistry: treat all 404 errors as not-found
Despite the fact that the OCI specification explicitly lists the error codes that a registry should return, it seems that some registries do not follow the spec. Given that 404 is a reasonably unambiguous error, it seems OK to treat all 404 errors as if they were returning one of the standard not-found errors. Also change `GetModule` so that it also treats "not found" responses in the same way as the `ModuleVersions` call. Fixes #2982 Signed-off-by: Roger Peppe <[email protected]> Change-Id: I96a4e28f38ad1fcf78c0be70a6aaa38713641123 Reviewed-on: https://review.gerrithub.io/c/cue-lang/cue/+/1194495 Reviewed-by: Daniel Martí <[email protected]> Unity-Result: CUE porcuepine <[email protected]> TryBot-Result: CUEcueckoo <[email protected]>
1 parent a169ae2 commit bf75699

File tree

6 files changed

+53
-7
lines changed

6 files changed

+53
-7
lines changed

cmd/cue/cmd/testdata/script/registry_publish.txtar

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ cmp stdout ../expect-eval-stdout
1313
env CUE_REGISTRY=$ORIG_CUE_REGISTRY
1414
env CUE_CACHE_DIR=$WORK/.tmp/different-cache
1515
! exec cue eval
16-
stderr 'repository name not known to registry'
16+
stderr 'cannot fetch [email protected]: module [email protected]: module not found'
1717

1818
-- expect-eval-stdout --
1919
main: "main"

cmd/cue/cmd/testdata/script/registry_wrong_prefix.txtar

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ env CUE_REGISTRY=$DEBUG_REGISTRY_HOST/something+insecure
33
cmp stderr expect-stderr
44

55
-- expect-stderr --
6-
import failed: cannot find package "example.com/e": cannot fetch example.com/[email protected]: module example.com/[email protected]: 404 Not Found: name unknown: repository name not known to registry:
6+
import failed: cannot find package "example.com/e": cannot fetch example.com/[email protected]: module example.com/[email protected]: module not found:
77
./main.cue:2:8
88
-- main.cue --
99
package main

cue/load/testdata/testfetch/depnotfound.txtar

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
-- out/modfetch/error --
2-
import failed: cannot find package "example.com": cannot fetch [email protected]: module [email protected]: 404 Not Found: name unknown: repository name not known to registry:
2+
import failed: cannot find package "example.com": cannot fetch [email protected]: module [email protected]: module not found:
33
./main.cue:2:8
44
-- cue.mod/module.cue --
55
module: "main.org@v0"

internal/mod/modrequirements/requirements_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ deps: "bar.com@v0": v: "v0.0.2" // doesn't exist
123123
"foo.com/bar/[email protected]",
124124
), nil)
125125
_, err := rs.Graph(ctx)
126-
qt.Assert(t, qt.ErrorMatches(err, `[email protected]: module [email protected]: 404 Not Found: name unknown: repository name not known to registry`))
126+
qt.Assert(t, qt.ErrorMatches(err, `[email protected]: module [email protected]: module not found`))
127127
qt.Assert(t, qt.ErrorAs(err, new(*mvs.BuildListError[module.Version])))
128128
}
129129

mod/modregistry/client.go

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -118,8 +118,7 @@ func (c *Client) GetModule(ctx context.Context, m module.Version) (*Module, erro
118118
}
119119
rd, err := loc.Registry.GetTag(ctx, loc.Repository, loc.Tag)
120120
if err != nil {
121-
// TODO should we use isNotExist here too?
122-
if errors.Is(err, ociregistry.ErrManifestUnknown) {
121+
if isNotExist(err) {
123122
return nil, fmt.Errorf("module %v: %w", m, ErrNotFound)
124123
}
125124
return nil, fmt.Errorf("module %v: %w", m, err)
@@ -450,8 +449,14 @@ func isNotExist(err error) bool {
450449
// without explicitly including a "denied" error code.
451450
// We treat this as a "not found" error because there's
452451
// nothing the user can do about it.
452+
//
453+
// Also, some registries return an invalid error code with a 404
454+
// response (see https://cuelang.org/issue/2982), so it
455+
// seems reasonable to treat that as a non-found error too.
453456
if herr := ociregistry.HTTPError(nil); errors.As(err, &herr) {
454-
return herr.StatusCode() == http.StatusForbidden
457+
statusCode := herr.StatusCode()
458+
return statusCode == http.StatusForbidden ||
459+
statusCode == http.StatusNotFound
455460
}
456461
return false
457462
}

mod/modregistry/client_test.go

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ import (
1919
"context"
2020
"fmt"
2121
"io"
22+
"net/http"
23+
"net/http/httptest"
24+
"net/url"
2225
"os"
2326
"path"
2427
"testing"
@@ -28,6 +31,7 @@ import (
2831

2932
"golang.org/x/tools/txtar"
3033

34+
"cuelabs.dev/go/oci/ociregistry/ociclient"
3135
"cuelabs.dev/go/oci/ociregistry/ocimem"
3236

3337
"cuelang.org/go/internal/mod/semver"
@@ -127,6 +131,43 @@ x: a.foo + something.bar
127131
qt.Assert(t, qt.DeepEquals(tags, []string{"v0.5.100"}))
128132
}
129133

134+
func TestNotFound(t *testing.T) {
135+
// Check that we get appropriate not-found behavior when the
136+
// HTTP response isn't entirely according to spec.
137+
// See https://cuelang.org/issue/2982 for an example.
138+
var writeResponse func(w http.ResponseWriter)
139+
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
140+
writeResponse(w)
141+
}))
142+
defer srv.Close()
143+
u, _ := url.Parse(srv.URL)
144+
reg, err := ociclient.New(u.Host, &ociclient.Options{
145+
Insecure: true,
146+
})
147+
qt.Assert(t, qt.IsNil(err))
148+
client := NewClient(reg)
149+
checkNotFound := func(writeResp func(w http.ResponseWriter)) {
150+
ctx := context.Background()
151+
writeResponse = writeResp
152+
mv := module.MustNewVersion("foo.com/bar@v1", "v1.2.3")
153+
_, err := client.GetModule(ctx, mv)
154+
qt.Assert(t, qt.ErrorIs(err, ErrNotFound))
155+
versions, err := client.ModuleVersions(ctx, "foo/bar")
156+
qt.Assert(t, qt.IsNil(err))
157+
qt.Assert(t, qt.HasLen(versions, 0))
158+
}
159+
160+
checkNotFound(func(w http.ResponseWriter) {
161+
// issue 2982
162+
w.WriteHeader(http.StatusNotFound)
163+
w.Write([]byte(`{"errors":[{"code":"NOT_FOUND","message":"repository playground/cue/github.com not found"}]}`))
164+
})
165+
checkNotFound(func(w http.ResponseWriter) {
166+
w.WriteHeader(http.StatusForbidden)
167+
w.Write([]byte(`some other message`))
168+
})
169+
}
170+
130171
func TestPutWithMetadata(t *testing.T) {
131172
const testMod = `
132173
-- cue.mod/module.cue --

0 commit comments

Comments
 (0)