mirror of
https://github.com/moby/moby.git
synced 2026-01-11 18:51:37 +00:00
These comments were added to enforce using the correct import path for
our packages ("github.com/docker/docker", not "github.com/moby/moby").
However, when working in go module mode (not GOPATH / vendor), they have
no effect, so their impact is limited.
Remove these imports in preparation of migrating our code to become an
actual go module.
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
184 lines
4.9 KiB
Go
184 lines
4.9 KiB
Go
package distribution
|
|
|
|
import (
|
|
"context"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"net/url"
|
|
"runtime"
|
|
"strings"
|
|
"sync/atomic"
|
|
"testing"
|
|
|
|
"github.com/distribution/reference"
|
|
registrytypes "github.com/docker/docker/api/types/registry"
|
|
"github.com/docker/docker/image"
|
|
"github.com/docker/docker/registry"
|
|
"github.com/opencontainers/go-digest"
|
|
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
|
"gotest.tools/v3/assert"
|
|
is "gotest.tools/v3/assert/cmp"
|
|
)
|
|
|
|
func TestNoMatchesErr(t *testing.T) {
|
|
err := noMatchesErr{}
|
|
assert.Check(t, is.ErrorContains(err, "no matching manifest for "+runtime.GOOS))
|
|
|
|
err = noMatchesErr{ocispec.Platform{
|
|
Architecture: "arm64",
|
|
OS: "windows",
|
|
OSVersion: "10.0.17763",
|
|
Variant: "v8",
|
|
}}
|
|
assert.Check(t, is.Error(err, "no matching manifest for windows(10.0.17763)/arm64/v8 in the manifest list entries"))
|
|
}
|
|
|
|
func TestPullSchema2Config(t *testing.T) {
|
|
ctx := context.Background()
|
|
|
|
const imageJSON = `{
|
|
"architecture": "amd64",
|
|
"os": "linux",
|
|
"config": {},
|
|
"rootfs": {
|
|
"type": "layers",
|
|
"diff_ids": []
|
|
}
|
|
}`
|
|
expectedDigest := digest.Digest("sha256:66ad98165d38f53ee73868f82bd4eed60556ddfee824810a4062c4f777b20a5b")
|
|
|
|
tests := []struct {
|
|
name string
|
|
handler func(callCount int, w http.ResponseWriter)
|
|
expectError string
|
|
expectAttempts uint64
|
|
}{
|
|
{
|
|
name: "success first time",
|
|
handler: func(callCount int, w http.ResponseWriter) {
|
|
w.WriteHeader(http.StatusOK)
|
|
_, _ = w.Write([]byte(imageJSON))
|
|
},
|
|
expectAttempts: 1,
|
|
},
|
|
{
|
|
name: "500 status",
|
|
handler: func(callCount int, w http.ResponseWriter) {
|
|
if callCount == 1 {
|
|
w.WriteHeader(http.StatusInternalServerError)
|
|
return
|
|
}
|
|
w.WriteHeader(http.StatusOK)
|
|
_, _ = w.Write([]byte(imageJSON))
|
|
},
|
|
expectAttempts: 2,
|
|
},
|
|
{
|
|
name: "EOF",
|
|
handler: func(callCount int, w http.ResponseWriter) {
|
|
if callCount == 1 {
|
|
panic("intentional panic")
|
|
}
|
|
w.WriteHeader(http.StatusOK)
|
|
_, _ = w.Write([]byte(imageJSON))
|
|
},
|
|
expectAttempts: 2,
|
|
},
|
|
{
|
|
name: "unauthorized",
|
|
handler: func(callCount int, w http.ResponseWriter) {
|
|
w.WriteHeader(http.StatusUnauthorized)
|
|
_, _ = w.Write([]byte("you need to be authenticated"))
|
|
},
|
|
expectError: "unauthorized: you need to be authenticated",
|
|
expectAttempts: 1,
|
|
},
|
|
{
|
|
name: "unauthorized JSON",
|
|
handler: func(callCount int, w http.ResponseWriter) {
|
|
w.Header().Set("Content-Type", "application/json")
|
|
w.WriteHeader(http.StatusUnauthorized)
|
|
_, _ = w.Write([]byte(` { "errors": [{"code": "UNAUTHORIZED", "message": "you need to be authenticated", "detail": "more detail"}]}`))
|
|
},
|
|
expectError: "unauthorized: you need to be authenticated",
|
|
expectAttempts: 1,
|
|
},
|
|
{
|
|
name: "unauthorized JSON no body",
|
|
handler: func(callCount int, w http.ResponseWriter) {
|
|
w.Header().Set("Content-Type", "application/json")
|
|
w.WriteHeader(http.StatusUnauthorized)
|
|
},
|
|
expectError: "unauthorized: authentication required",
|
|
expectAttempts: 1,
|
|
},
|
|
}
|
|
|
|
for _, tc := range tests {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
var callCount atomic.Uint64
|
|
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
t.Logf("HTTP %s %s", r.Method, r.URL.Path)
|
|
defer r.Body.Close()
|
|
switch {
|
|
case r.Method == http.MethodGet && r.URL.Path == "/v2":
|
|
w.WriteHeader(http.StatusOK)
|
|
case r.Method == http.MethodGet && r.URL.Path == "/v2/library/testremotename/blobs/"+expectedDigest.String():
|
|
tc.handler(int(callCount.Add(1)), w)
|
|
default:
|
|
w.WriteHeader(http.StatusNotFound)
|
|
}
|
|
}))
|
|
defer ts.Close()
|
|
|
|
p := testNewPuller(t, ts.URL)
|
|
|
|
config, err := p.pullSchema2Config(ctx, expectedDigest)
|
|
if tc.expectError == "" {
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
_, err = image.NewFromJSON(config)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
} else {
|
|
if err == nil {
|
|
t.Fatalf("expected error to contain %q", tc.expectError)
|
|
}
|
|
if !strings.Contains(err.Error(), tc.expectError) {
|
|
t.Fatalf("expected error=%q to contain %q", err, tc.expectError)
|
|
}
|
|
}
|
|
|
|
if cc := callCount.Load(); cc != tc.expectAttempts {
|
|
t.Fatalf("got callCount=%d but expected=%d", cc, tc.expectAttempts)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func testNewPuller(t *testing.T, rawurl string) *puller {
|
|
t.Helper()
|
|
|
|
uri, err := url.Parse(rawurl)
|
|
assert.NilError(t, err, "could not parse url from test server: %v", rawurl)
|
|
|
|
repoName, err := reference.ParseNormalizedNamed("testremotename")
|
|
assert.NilError(t, err)
|
|
|
|
imagePullConfig := &ImagePullConfig{
|
|
Config: Config{
|
|
AuthConfig: ®istrytypes.AuthConfig{
|
|
RegistryToken: secretRegistryToken,
|
|
},
|
|
},
|
|
}
|
|
|
|
p := newPuller(registry.APIEndpoint{URL: uri}, repoName, imagePullConfig, nil)
|
|
p.repo, err = newRepository(context.Background(), repoName, p.endpoint, p.config.MetaHeaders, p.config.AuthConfig, "pull")
|
|
assert.NilError(t, err)
|
|
return p
|
|
}
|