diff --git a/docker/docker_client.go b/docker/docker_client.go index 797be45a2c..c1a1dbd11b 100644 --- a/docker/docker_client.go +++ b/docker/docker_client.go @@ -67,6 +67,9 @@ var ( } ) +// httpWrapper allows replacing the used http.RoundTripper. This is internal-only, used for github.com/dnaeon/go-vcr/recorder -based tests. +type httpWrapper func(http.RoundTripper) http.RoundTripper + // extensionSignature and extensionSignatureList come from github.com/openshift/origin/pkg/dockerregistry/server/signaturedispatcher.go: // signature represents a Docker image signature. type extensionSignature struct { @@ -92,8 +95,9 @@ type bearerToken struct { // dockerClient is configuration for dealing with a single Docker registry. type dockerClient struct { // The following members are set by newDockerClient and do not change afterwards. - sys *types.SystemContext - registry string + sys *types.SystemContext + httpWrapper httpWrapper // nil unless running tests + registry string // tlsClientConfig is setup by newDockerClient and will be used and updated // by detectProperties(). Callers can edit tlsClientConfig.InsecureSkipVerify in the meantime. @@ -210,7 +214,7 @@ func dockerCertDir(sys *types.SystemContext, hostPort string) (string, error) { // newDockerClientFromRef returns a new dockerClient instance for refHostname (a host a specified in the Docker image reference, not canonicalized to dockerRegistry) // “write” specifies whether the client will be used for "write" access (in particular passed to lookaside.go:toplevelFromSection) // signatureBase is always set in the return value -func newDockerClientFromRef(sys *types.SystemContext, ref dockerReference, write bool, actions string) (*dockerClient, error) { +func newDockerClientFromRef(sys *types.SystemContext, ref dockerReference, write bool, actions string, httpWrapper httpWrapper) (*dockerClient, error) { registry := reference.Domain(ref.ref) auth, err := config.GetCredentials(sys, registry) if err != nil { @@ -222,7 +226,7 @@ func newDockerClientFromRef(sys *types.SystemContext, ref dockerReference, write return nil, err } - client, err := newDockerClient(sys, registry, ref.ref.Name()) + client, err := newDockerClient(sys, registry, ref.ref.Name(), httpWrapper) if err != nil { return nil, err } @@ -242,7 +246,7 @@ func newDockerClientFromRef(sys *types.SystemContext, ref dockerReference, write // (e.g., "registry.com[:5000][/some/namespace]/repo"). // Please note that newDockerClient does not set all members of dockerClient // (e.g., username and password); those must be set by callers if necessary. -func newDockerClient(sys *types.SystemContext, registry, reference string) (*dockerClient, error) { +func newDockerClient(sys *types.SystemContext, registry, reference string, httpWrapper httpWrapper) (*dockerClient, error) { hostName := registry if registry == dockerHostname { registry = dockerRegistry @@ -279,6 +283,7 @@ func newDockerClient(sys *types.SystemContext, registry, reference string) (*doc return &dockerClient{ sys: sys, + httpWrapper: httpWrapper, registry: registry, tlsClientConfig: tlsClientConfig, }, nil @@ -287,7 +292,7 @@ func newDockerClient(sys *types.SystemContext, registry, reference string) (*doc // CheckAuth validates the credentials by attempting to log into the registry // returns an error if an error occurred while making the http request or the status code received was 401 func CheckAuth(ctx context.Context, sys *types.SystemContext, username, password, registry string) error { - client, err := newDockerClient(sys, registry, registry) + client, err := newDockerClient(sys, registry, registry, nil) if err != nil { return errors.Wrapf(err, "error creating new docker client") } @@ -349,7 +354,7 @@ func SearchRegistry(ctx context.Context, sys *types.SystemContext, registry, ima hostname = dockerV1Hostname } - client, err := newDockerClient(sys, hostname, registry) + client, err := newDockerClient(sys, hostname, registry, nil) if err != nil { return nil, errors.Wrapf(err, "error creating new docker client") } @@ -723,7 +728,11 @@ func (c *dockerClient) detectPropertiesHelper(ctx context.Context) error { } tr := tlsclientconfig.NewTransport() tr.TLSClientConfig = c.tlsClientConfig - c.client = &http.Client{Transport: tr} + rt := http.RoundTripper(tr) + if c.httpWrapper != nil { + rt = c.httpWrapper(rt) + } + c.client = &http.Client{Transport: rt} ping := func(scheme string) error { url := fmt.Sprintf(resolvedPingV2URL, scheme, c.registry) diff --git a/docker/docker_client_test.go b/docker/docker_client_test.go index 106bf62b9e..e794c4b582 100644 --- a/docker/docker_client_test.go +++ b/docker/docker_client_test.go @@ -1,12 +1,26 @@ package docker +// Many of these tests are made using github.com/dnaeon/go-vcr/recorder, and under ordinary +// operation are expected to run completely off-line from the recorded interactions. +// +// To update an individual test, set up a registry server as needed (usually just +// allow access to docker.io; special setup will be described with individual tests), +// temporarily edit the test to use recorder.ModeRecording, run the test. +// Don’t forget to revert to recorder.ModeReplaying! + import ( + "context" "fmt" + "io/ioutil" + "net/http" + "os" "path/filepath" "testing" "time" "github.com/containers/image/v5/types" + "github.com/dnaeon/go-vcr/recorder" + digest "github.com/opencontainers/go-digest" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -151,3 +165,138 @@ func assertBearerTokensEqual(t *testing.T, expected, subject *bearerToken) { t.Fatalf("expected [%s] to equal [%s], it did not", subject.IssuedAt, expected.IssuedAt) } } + +// prepareVCR is a shared helper for setting up HTTP request/response recordings using recordingBaseName. +// It returns the result of preparing ctx and ref, a httpWrapper and a cleanup callback. +func prepareVCR(t *testing.T, ctx *types.SystemContext, recordingBaseName string, mode recorder.Mode, + ref string) (*types.SystemContext, httpWrapper, func(), dockerReference) { + // Always set ctx.DockerAuthConfig so that we don’t depend on $HOME. + ourCtx := types.SystemContext{} + if ctx != nil { + ourCtx = *ctx + } + if ourCtx.DockerAuthConfig == nil { + ourCtx.DockerAuthConfig = &types.DockerAuthConfig{} + } + + parsedRef, err := ParseReference(ref) + require.NoError(t, err) + dockerRef, ok := parsedRef.(dockerReference) + require.True(t, ok) + + // dockerClient creates a new http.Client in each call to getBearerToken, so we need + // not just a single recording with a given name, but a sequence of recordings. + recorderNo := 0 + allRecorders := []*recorder.Recorder{} + httpWrapper := func(rt http.RoundTripper) http.RoundTripper { + recordingName := fmt.Sprintf("fixtures/recording-%s-%d", recordingBaseName, recorderNo) + recorderNo++ + + // Always create the file first; without that, even with mode == recorder.ModeReplaying, + // the recorder is in recording mode. We want to ensure that recording happens only + // as an intentional decision. + recordingFileName := recordingName + ".yaml" + f, err := os.OpenFile(recordingFileName, os.O_RDWR|os.O_CREATE, 0600) + require.NoError(t, err) + f.Close() + + r, err := recorder.NewAsMode(recordingName, mode, rt) + require.NoError(t, err) + allRecorders = append(allRecorders, r) + return r + } + + closeRecorders := func() { + for _, r := range allRecorders { + err := r.Stop() + require.NoError(t, err) + } + } + + return &ourCtx, httpWrapper, closeRecorders, dockerRef +} + +// vcrDockerClient creates a dockerClient using a series of HTTP request/response recordings +// using recordingBaseName. +// It returns a dockerClient and a cleanup callback, and the parsed version of ref. +func vcrDockerClient(t *testing.T, ctx *types.SystemContext, recordingBaseName string, mode recorder.Mode, + ref string, write bool, actions string) (*dockerClient, func(), dockerReference) { + ctx, httpWrapper, cleanup, dockerRef := prepareVCR(t, ctx, recordingBaseName, mode, + ref) + + client, err := newDockerClientFromRef(ctx, dockerRef, write, actions, httpWrapper) + require.NoError(t, err) + return client, cleanup, dockerRef +} + +func TestDockerClientDetectProperties(t *testing.T) { + // Success, against the Docker Hub + client, cleanup, _ := vcrDockerClient(t, nil, "detectProperties-docker.io", recorder.ModeReplaying, + "//busybox:latest", false, "pull") + defer cleanup() + err := client.detectProperties(context.Background()) + require.NoError(t, err) + assert.Equal(t, "https", client.scheme) + assert.Equal(t, []challenge{{ + Scheme: "bearer", + Parameters: map[string]string{"realm": "https://auth.docker.io/token", "service": "registry.docker.io"}, + }}, client.challenges) + assert.False(t, client.supportsSignatures) + + // Success, against Atomic Registry. + // See the comment above TestDockerClientGetExtensionsSignatures for instructions on setting up the recording. + openshiftCtx := &types.SystemContext{ + DockerInsecureSkipTLSVerify: types.OptionalBoolTrue, + } + client, cleanup, _ = vcrDockerClient(t, openshiftCtx, "detectProperties-openshift", recorder.ModeReplaying, + "//localhost:5000/myns/personal:personal", false, "pull") + defer cleanup() + err = client.detectProperties(context.Background()) + require.NoError(t, err) + assert.Equal(t, "http", client.scheme) + assert.Equal(t, []challenge{{ + Scheme: "bearer", + Parameters: map[string]string{"realm": "http://localhost:5000/openshift/token"}, + }}, client.challenges) + assert.True(t, client.supportsSignatures) + + // TODO? Test the various other cases, e.g. a schema1 registry +} + +// To record the the X-Registry-Supports-Signatures tests, +// use skopeo's integration tests to set up an Atomic Registry per https://github.com/projectatomic/skopeo/pull/320 +// except running the container with -p 5000:5000, e.g. +// (sudo docker run --rm -i -t -p 5000:5000 "skopeo-dev:openshift-shell" bash) +// Then set: +// - the username:password values obtained by decoding "auth" from the in-container ~/.docker/config.json +// - the manifest digest reference e.g. from (oc get istag personal:personal) value image.dockerImageReference in-container. +// - the signature name from the same (oc get istag personal:personal) +func TestDockerClientGetExtensionsSignatures(t *testing.T) { + ctx := &types.SystemContext{ + DockerAuthConfig: &types.DockerAuthConfig{ + Username: "unused", + Password: "dh2juhu6LbGYGSHKMUa5BFEpyoPMYDVA59hxd3FCfbU", + }, + DockerInsecureSkipTLSVerify: types.OptionalBoolTrue, + } + + // Success + manifestDigest := digest.Digest("sha256:8d7fe3e157e56648ab790794970fbdfe82c84af79e807443b98df92c822a9b9b") + client, cleanup, dockerRef := vcrDockerClient(t, ctx, "getExtensionsSignatures-success", recorder.ModeReplaying, + "//localhost:5000/myns/personal:personal", false, "pull") + defer cleanup() + esl, err := client.getExtensionsSignatures(context.Background(), dockerRef, manifestDigest) + require.NoError(t, err) + expectedSignature, err := ioutil.ReadFile("fixtures/extension-personal-personal.signature") + require.NoError(t, err) + assert.Equal(t, &extensionSignatureList{ + Signatures: []extensionSignature{{ + Version: extensionSignatureSchemaVersion, + Name: manifestDigest.String() + "@809439d23da88df57186b0f2fce91e9a", + Type: extensionSignatureTypeAtomic, + Content: expectedSignature, + }}, + }, esl) + + // TODO? Test the various failure modes. +} diff --git a/docker/docker_image.go b/docker/docker_image.go index 0c1cee0d32..c6a12fead7 100644 --- a/docker/docker_image.go +++ b/docker/docker_image.go @@ -27,7 +27,7 @@ type Image struct { // a client to the registry hosting the given image. // The caller must call .Close() on the returned Image. func newImage(ctx context.Context, sys *types.SystemContext, ref dockerReference) (types.ImageCloser, error) { - s, err := newImageSource(ctx, sys, ref) + s, err := newImageSource(ctx, sys, ref, nil) if err != nil { return nil, err } @@ -60,7 +60,7 @@ func GetRepositoryTags(ctx context.Context, sys *types.SystemContext, ref types. } path := fmt.Sprintf(tagsPath, reference.Path(dr.ref)) - client, err := newDockerClientFromRef(sys, dr, false, "pull") + client, err := newDockerClientFromRef(sys, dr, false, "pull", nil) if err != nil { return nil, errors.Wrap(err, "failed to create client") } @@ -124,7 +124,7 @@ func GetDigest(ctx context.Context, sys *types.SystemContext, ref types.ImageRef return "", err } - client, err := newDockerClientFromRef(sys, dr, false, "pull") + client, err := newDockerClientFromRef(sys, dr, false, "pull", nil) if err != nil { return "", errors.Wrap(err, "failed to create client") } diff --git a/docker/docker_image_dest.go b/docker/docker_image_dest.go index 842dcfba68..5bb842f72c 100644 --- a/docker/docker_image_dest.go +++ b/docker/docker_image_dest.go @@ -37,8 +37,8 @@ type dockerImageDestination struct { } // newImageDestination creates a new ImageDestination for the specified image reference. -func newImageDestination(sys *types.SystemContext, ref dockerReference) (types.ImageDestination, error) { - c, err := newDockerClientFromRef(sys, ref, true, "pull,push") +func newImageDestination(sys *types.SystemContext, ref dockerReference, httpWrapper httpWrapper) (*dockerImageDestination, error) { + c, err := newDockerClientFromRef(sys, ref, true, "pull,push", httpWrapper) if err != nil { return nil, err } diff --git a/docker/docker_image_dest_test.go b/docker/docker_image_dest_test.go new file mode 100644 index 0000000000..5aa5cefb98 --- /dev/null +++ b/docker/docker_image_dest_test.go @@ -0,0 +1,80 @@ +package docker + +// Many of these tests are made using github.com/dnaeon/go-vcr/recorder. +// See docker_client_test.go for more instructions. + +import ( + "context" + "io/ioutil" + "testing" + + "github.com/containers/image/v5/types" + "github.com/dnaeon/go-vcr/recorder" + digest "github.com/opencontainers/go-digest" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// TODO: Tests for quite a few methods. + +// vcrImageDestination creates a dockerImageDestination using a series of HTTP request/response recordings +// using recordingBaseName. +// It returns the imageDestination and a cleanup callback +func vcrImageDestination(t *testing.T, ctx *types.SystemContext, recordingBaseName string, mode recorder.Mode, + ref string) (*dockerImageDestination, func()) { + ctx, httpWrapper, cleanup, dockerRef := prepareVCR(t, ctx, recordingBaseName, mode, + ref) + + dest, err := newImageDestination(ctx, dockerRef, httpWrapper) + require.NoError(t, err) + return dest, cleanup +} + +// See the comment above TestDockerClientGetExtensionsSignatures for instructions on setting up the recording. +func TestDockerImageDestinationPutSignaturesToAPIExtension(t *testing.T) { + ctx := &types.SystemContext{ + DockerAuthConfig: &types.DockerAuthConfig{ + Username: "unused", + Password: "dh2juhu6LbGYGSHKMUa5BFEpyoPMYDVA59hxd3FCfbU", + }, + DockerInsecureSkipTLSVerify: types.OptionalBoolTrue, + } + expectedSignature1, err := ioutil.ReadFile("fixtures/extension-personal-personal.signature") + require.NoError(t, err) + + // Success + dest, cleanup := vcrImageDestination(t, ctx, "putSignaturesToAPIExtension-success", recorder.ModeReplaying, + "//localhost:5000/myns/personal:personal") + defer cleanup() + // The value can be obtained e.g. from (oc get istag personal:personal) value image.dockerImageReference in-container. + manifestDigest := digest.Digest("sha256:8d7fe3e157e56648ab790794970fbdfe82c84af79e807443b98df92c822a9b9b") + sig2 := []byte("This is not really a signature") + err = dest.putSignaturesToAPIExtension(context.Background(), [][]byte{sig2}, manifestDigest) + require.NoError(t, err) + // Verify that this preserves the original signature and creates a new one. + esl, err := dest.c.getExtensionsSignatures(context.Background(), dest.ref, manifestDigest) + require.NoError(t, err) + // We do not know what extensionSignature.Name has been randomly generated, + // so only verify that it has the expected format, and then replace it for the purposes of equality comparison. + require.Len(t, esl.Signatures, 2) + assert.Regexp(t, manifestDigest.String()+"@.{32}", esl.Signatures[1].Name) + assert.Equal(t, &extensionSignatureList{ + Signatures: []extensionSignature{ + { + Version: extensionSignatureSchemaVersion, + Name: manifestDigest.String() + "@809439d23da88df57186b0f2fce91e9a", + Type: extensionSignatureTypeAtomic, + Content: expectedSignature1, + }, + { + Version: extensionSignatureSchemaVersion, + Name: esl.Signatures[1].Name, // This is comparing the value with itself, i.e. ignoring the comparison; we have checked the format above. + Type: extensionSignatureTypeAtomic, + Content: sig2, + }, + }, + }, esl) + + // TODO? Test that unknown signature kinds are silently ignored. + // TODO? Test the various failure modes. +} diff --git a/docker/docker_image_src.go b/docker/docker_image_src.go index 2fe68ea27a..dd9d1e6057 100644 --- a/docker/docker_image_src.go +++ b/docker/docker_image_src.go @@ -33,7 +33,7 @@ type dockerImageSource struct { // newImageSource creates a new ImageSource for the specified image reference. // The caller must call .Close() on the returned ImageSource. -func newImageSource(ctx context.Context, sys *types.SystemContext, ref dockerReference) (*dockerImageSource, error) { +func newImageSource(ctx context.Context, sys *types.SystemContext, ref dockerReference, httpWrapper httpWrapper) (*dockerImageSource, error) { registry, err := sysregistriesv2.FindRegistry(sys, ref.ref.Name()) if err != nil { return nil, errors.Wrapf(err, "error loading registries configuration") @@ -70,7 +70,7 @@ func newImageSource(ctx context.Context, sys *types.SystemContext, ref dockerRef logrus.Debugf("Trying to access %q", pullSource.Reference) } logrus.Debugf("Trying to access %q", pullSource.Reference) - s, err := newImageSourceAttempt(ctx, sys, ref, pullSource) + s, err := newImageSourceAttempt(ctx, sys, ref, pullSource, httpWrapper) if err == nil { return s, nil } @@ -101,7 +101,7 @@ func newImageSource(ctx context.Context, sys *types.SystemContext, ref dockerRef // newImageSourceAttempt is an internal helper for newImageSource. Everyone else must call newImageSource. // Given a logicalReference and a pullSource, return a dockerImageSource if it is reachable. // The caller must call .Close() on the returned ImageSource. -func newImageSourceAttempt(ctx context.Context, sys *types.SystemContext, logicalRef dockerReference, pullSource sysregistriesv2.PullSource) (*dockerImageSource, error) { +func newImageSourceAttempt(ctx context.Context, sys *types.SystemContext, logicalRef dockerReference, pullSource sysregistriesv2.PullSource, httpWrapper httpWrapper) (*dockerImageSource, error) { physicalRef, err := newReference(pullSource.Reference) if err != nil { return nil, err @@ -116,7 +116,7 @@ func newImageSourceAttempt(ctx context.Context, sys *types.SystemContext, logica endpointSys = © } - client, err := newDockerClientFromRef(endpointSys, physicalRef, false, "pull") + client, err := newDockerClientFromRef(endpointSys, physicalRef, false, "pull", httpWrapper) if err != nil { return nil, err } @@ -430,7 +430,7 @@ func deleteImage(ctx context.Context, sys *types.SystemContext, ref dockerRefere // OpenShift ignores the action string (both the password and the token is an OpenShift API token identifying a user). // // We have to hard-code a single string, luckily both docker/distribution and quay.io support "*" to mean "everything". - c, err := newDockerClientFromRef(sys, ref, true, "*") + c, err := newDockerClientFromRef(sys, ref, true, "*", nil) if err != nil { return err } diff --git a/docker/docker_image_src_test.go b/docker/docker_image_src_test.go index bb55659d23..300c77c438 100644 --- a/docker/docker_image_src_test.go +++ b/docker/docker_image_src_test.go @@ -1,5 +1,8 @@ package docker +// Many of these tests are made using github.com/dnaeon/go-vcr/recorder. +// See docker_client_test.go for more instructions. + import ( "context" "io/ioutil" @@ -12,6 +15,7 @@ import ( "testing" "github.com/containers/image/v5/types" + "github.com/dnaeon/go-vcr/recorder" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -79,6 +83,10 @@ location = "@REGISTRY@/with-mirror" } } +// TODO: TestNewImageSource +// TODO: TestDockerImageSourceReference +// TODO: TestDockerImageSourceClose + func TestSimplifyContentType(t *testing.T) { for _, c := range []struct{ input, expected string }{ {"", ""}, @@ -95,3 +103,64 @@ func TestSimplifyContentType(t *testing.T) { assert.Equal(t, c.expected, out, c.input) } } + +// vcrImageSource creates a dockerImageSource using a series of HTTP request/response recordings +// using recordingBaseName. +// It returns the imageSource or an error, and a cleanup callback +func vcrImageSource(t *testing.T, ctx *types.SystemContext, recordingBaseName string, mode recorder.Mode, + ref string) (*dockerImageSource, func(), error) { + ctx, httpWrapper, cleanup, dockerRef := prepareVCR(t, ctx, recordingBaseName, mode, + ref) + + src, err := newImageSource(context.Background(), ctx, dockerRef, httpWrapper) + return src, cleanup, err +} + +func TestDockerImageSourceGetManifest(t *testing.T) { + // Success + src, cleanup, err := vcrImageSource(t, nil, "GetManifest-success", recorder.ModeReplaying, + "//busybox:latest") + defer cleanup() + require.NoError(t, err) + manifest, mimeType, err := src.GetManifest(context.Background(), nil) + require.NoError(t, err) + // Whatever was returned is now cached + assert.Equal(t, src.cachedManifest, manifest) + assert.Equal(t, src.cachedManifestMIMEType, mimeType) + // TODO: Somehow test caching (i.e. that we don’t redo the request when doing it the second time) + + // Test failure fetching the manifest + _, cleanup, err = vcrImageSource(t, nil, "GetManifest-not-found", recorder.ModeReplaying, + "//busybox:this-does-not-exist") + defer cleanup() + assert.Error(t, err) +} + +// TODO: Tests for quite a few methods. + +// See the comment above TestDockerClientGetExtensionsSignatures for instructions on setting up the recording. +func TestDockerImageSourceGetSignaturesFromAPIExtension(t *testing.T) { + ctx := &types.SystemContext{ + DockerAuthConfig: &types.DockerAuthConfig{ + Username: "unused", + Password: "dh2juhu6LbGYGSHKMUa5BFEpyoPMYDVA59hxd3FCfbU", + }, + DockerInsecureSkipTLSVerify: types.OptionalBoolTrue, + } + + // Success + // This only tests getting a single signature; the multiple-signature case + // is tested within TestDockerImageDestinationPutSignaturesToAPIExtension. + src, cleanup, err := vcrImageSource(t, ctx, "getSignaturesFromAPIExtension-success", recorder.ModeReplaying, + "//localhost:5000/myns/personal:personal") + defer cleanup() + require.NoError(t, err) + sigs, err := src.getSignaturesFromAPIExtension(context.Background(), nil) + require.NoError(t, err) + expectedSignature, err := ioutil.ReadFile("fixtures/extension-personal-personal.signature") + require.NoError(t, err) + assert.Equal(t, [][]byte{expectedSignature}, sigs) + + // TODO? Test that unknown signature kinds are silently ignored. + // TODO? Test the various failure modes. +} diff --git a/docker/docker_transport.go b/docker/docker_transport.go index 8b8e579683..b9514617d1 100644 --- a/docker/docker_transport.go +++ b/docker/docker_transport.go @@ -141,13 +141,13 @@ func (ref dockerReference) NewImage(ctx context.Context, sys *types.SystemContex // NewImageSource returns a types.ImageSource for this reference. // The caller must call .Close() on the returned ImageSource. func (ref dockerReference) NewImageSource(ctx context.Context, sys *types.SystemContext) (types.ImageSource, error) { - return newImageSource(ctx, sys, ref) + return newImageSource(ctx, sys, ref, nil) } // NewImageDestination returns a types.ImageDestination for this reference. // The caller must call .Close() on the returned ImageDestination. func (ref dockerReference) NewImageDestination(ctx context.Context, sys *types.SystemContext) (types.ImageDestination, error) { - return newImageDestination(sys, ref) + return newImageDestination(sys, ref, nil) } // DeleteImage deletes the named image from the registry, if supported. diff --git a/docker/fixtures/extension-personal-personal.signature b/docker/fixtures/extension-personal-personal.signature new file mode 100644 index 0000000000..950e20e8e9 Binary files /dev/null and b/docker/fixtures/extension-personal-personal.signature differ diff --git a/docker/fixtures/recording-GetManifest-not-found-0.yaml b/docker/fixtures/recording-GetManifest-not-found-0.yaml new file mode 100644 index 0000000000..785e18a8e3 --- /dev/null +++ b/docker/fixtures/recording-GetManifest-not-found-0.yaml @@ -0,0 +1,82 @@ +--- +version: 1 +interactions: +- request: + body: "" + form: {} + headers: + Docker-Distribution-Api-Version: + - registry/2.0 + url: https://registry-1.docker.io/v2/ + method: GET + response: + body: | + {"errors":[{"code":"UNAUTHORIZED","message":"authentication required","detail":null}]} + headers: + Content-Length: + - "87" + Content-Type: + - application/json + Date: + - Wed, 11 Dec 2019 14:32:22 GMT + Docker-Distribution-Api-Version: + - registry/2.0 + Strict-Transport-Security: + - max-age=31536000 + Www-Authenticate: + - Bearer realm="https://auth.docker.io/token",service="registry.docker.io" + status: 401 Unauthorized + code: 401 + duration: "" +- request: + body: "" + form: {} + headers: {} + url: https://auth.docker.io/token?scope=repository%3Alibrary%2Fbusybox%3Apull&service=registry.docker.io + method: GET + response: + body: | + {"token":"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsIng1YyI6WyJNSUlDK2pDQ0FwK2dBd0lCQWdJQkFEQUtCZ2dxaGtqT1BRUURBakJHTVVRd1FnWURWUVFERXpzeVYwNVpPbFZMUzFJNlJFMUVVanBTU1U5Rk9reEhOa0U2UTFWWVZEcE5SbFZNT2tZelNFVTZOVkF5VlRwTFNqTkdPa05CTmxrNlNrbEVVVEFlRncweE9UQXhNVEl3TURJeU5EVmFGdzB5TURBeE1USXdNREl5TkRWYU1FWXhSREJDQmdOVkJBTVRPMUpMTkZNNlMwRkxVVHBEV0RWRk9rRTJSMVE2VTBwTVR6cFFNbEpMT2tOWlZVUTZTMEpEU0RwWFNVeE1Pa3hUU2xrNldscFFVVHBaVWxsRU1JSUJJakFOQmdrcWhraUc5dzBCQVFFRkFBT0NBUThBTUlJQkNnS0NBUUVBcjY2bXkveXpHN21VUzF3eFQ3dFplS2pqRzcvNnBwZFNMY3JCcko5VytwcndzMGtIUDVwUHRkMUpkcFdEWU1OZWdqQXhpUWtRUUNvd25IUnN2ODVUalBUdE5wUkdKVTRkeHJkeXBvWGc4TVhYUEUzL2lRbHhPS2VNU0prNlRKbG5wNGFtWVBHQlhuQXRoQzJtTlR5ak1zdFh2ZmNWN3VFYWpRcnlOVUcyUVdXQ1k1Ujl0a2k5ZG54Z3dCSEF6bG8wTzJCczFmcm5JbmJxaCtic3ZSZ1FxU3BrMWhxYnhSU3AyRlNrL2tBL1gyeUFxZzJQSUJxWFFMaTVQQ3krWERYZElJczV6VG9ZbWJUK0pmbnZaMzRLcG5mSkpNalpIRW4xUVJtQldOZXJZcVdtNVhkQVhUMUJrQU9aditMNFVwSTk3NFZFZ2ppY1JINVdBeWV4b1BFclRRSURBUUFCbzRHeU1JR3ZNQTRHQTFVZER3RUIvd1FFQXdJSGdEQVBCZ05WSFNVRUNEQUdCZ1JWSFNVQU1FUUdBMVVkRGdROUJEdFNTelJUT2t0QlMxRTZRMWcxUlRwQk5rZFVPbE5LVEU4NlVESlNTenBEV1ZWRU9rdENRMGc2VjBsTVREcE1VMHBaT2xwYVVGRTZXVkpaUkRCR0JnTlZIU01FUHpBOWdEc3lWMDVaT2xWTFMxSTZSRTFFVWpwU1NVOUZPa3hITmtFNlExVllWRHBOUmxWTU9rWXpTRVU2TlZBeVZUcExTak5HT2tOQk5sazZTa2xFVVRBS0JnZ3Foa2pPUFFRREFnTkpBREJHQWlFQXFOSXEwMFdZTmM5Z2tDZGdSUzRSWUhtNTRZcDBTa05Rd2lyMm5hSWtGd3dDSVFEMjlYdUl5TmpTa1cvWmpQaFlWWFB6QW9TNFVkRXNvUUhyUVZHMDd1N3ZsUT09Il19.eyJhY2Nlc3MiOlt7InR5cGUiOiJyZXBvc2l0b3J5IiwibmFtZSI6ImxpYnJhcnkvYnVzeWJveCIsImFjdGlvbnMiOlsicHVsbCJdfV0sImF1ZCI6InJlZ2lzdHJ5LmRvY2tlci5pbyIsImV4cCI6MTU3NjA3NTA0MiwiaWF0IjoxNTc2MDc0NzQyLCJpc3MiOiJhdXRoLmRvY2tlci5pbyIsImp0aSI6Imh2R3g1YlYzTTBfYnNaaEVrcS1UIiwibmJmIjoxNTc2MDc0NDQyLCJzdWIiOiIifQ.HYzG_ir5aYQAZlXrgaxCOOw9sGFNwzuunyO_7qEUbxWkM8V_3PnZxQypAYU9cnS66n293A-N1LbYBHRmiKq87LHq8MAdjvhfeTnqC-d0GV5WJOksN98x_T-eIBmdpFuAykTH1ZBsUA6OzAvuQqJ6zHy3GwgBpbFXVI0EZQHUELruNEkv8sgjWQeqG_U3sGbhN2wxWG51w9Mvp-9WvM_o4EcbUnzvYKVjJVQI-LjvVvI0b39SfZyKJkXDMGf-Op4SFo8MZw6edTUXyoIijQP1d7_Zfv3_i5nv-exUX9Ju4QPixlpSi2JwE5_2SJxIhddPRiGq9s6n-bFIdTN4WB0Rbw","access_token":"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsIng1YyI6WyJNSUlDK2pDQ0FwK2dBd0lCQWdJQkFEQUtCZ2dxaGtqT1BRUURBakJHTVVRd1FnWURWUVFERXpzeVYwNVpPbFZMUzFJNlJFMUVVanBTU1U5Rk9reEhOa0U2UTFWWVZEcE5SbFZNT2tZelNFVTZOVkF5VlRwTFNqTkdPa05CTmxrNlNrbEVVVEFlRncweE9UQXhNVEl3TURJeU5EVmFGdzB5TURBeE1USXdNREl5TkRWYU1FWXhSREJDQmdOVkJBTVRPMUpMTkZNNlMwRkxVVHBEV0RWRk9rRTJSMVE2VTBwTVR6cFFNbEpMT2tOWlZVUTZTMEpEU0RwWFNVeE1Pa3hUU2xrNldscFFVVHBaVWxsRU1JSUJJakFOQmdrcWhraUc5dzBCQVFFRkFBT0NBUThBTUlJQkNnS0NBUUVBcjY2bXkveXpHN21VUzF3eFQ3dFplS2pqRzcvNnBwZFNMY3JCcko5VytwcndzMGtIUDVwUHRkMUpkcFdEWU1OZWdqQXhpUWtRUUNvd25IUnN2ODVUalBUdE5wUkdKVTRkeHJkeXBvWGc4TVhYUEUzL2lRbHhPS2VNU0prNlRKbG5wNGFtWVBHQlhuQXRoQzJtTlR5ak1zdFh2ZmNWN3VFYWpRcnlOVUcyUVdXQ1k1Ujl0a2k5ZG54Z3dCSEF6bG8wTzJCczFmcm5JbmJxaCtic3ZSZ1FxU3BrMWhxYnhSU3AyRlNrL2tBL1gyeUFxZzJQSUJxWFFMaTVQQ3krWERYZElJczV6VG9ZbWJUK0pmbnZaMzRLcG5mSkpNalpIRW4xUVJtQldOZXJZcVdtNVhkQVhUMUJrQU9aditMNFVwSTk3NFZFZ2ppY1JINVdBeWV4b1BFclRRSURBUUFCbzRHeU1JR3ZNQTRHQTFVZER3RUIvd1FFQXdJSGdEQVBCZ05WSFNVRUNEQUdCZ1JWSFNVQU1FUUdBMVVkRGdROUJEdFNTelJUT2t0QlMxRTZRMWcxUlRwQk5rZFVPbE5LVEU4NlVESlNTenBEV1ZWRU9rdENRMGc2VjBsTVREcE1VMHBaT2xwYVVGRTZXVkpaUkRCR0JnTlZIU01FUHpBOWdEc3lWMDVaT2xWTFMxSTZSRTFFVWpwU1NVOUZPa3hITmtFNlExVllWRHBOUmxWTU9rWXpTRVU2TlZBeVZUcExTak5HT2tOQk5sazZTa2xFVVRBS0JnZ3Foa2pPUFFRREFnTkpBREJHQWlFQXFOSXEwMFdZTmM5Z2tDZGdSUzRSWUhtNTRZcDBTa05Rd2lyMm5hSWtGd3dDSVFEMjlYdUl5TmpTa1cvWmpQaFlWWFB6QW9TNFVkRXNvUUhyUVZHMDd1N3ZsUT09Il19.eyJhY2Nlc3MiOlt7InR5cGUiOiJyZXBvc2l0b3J5IiwibmFtZSI6ImxpYnJhcnkvYnVzeWJveCIsImFjdGlvbnMiOlsicHVsbCJdfV0sImF1ZCI6InJlZ2lzdHJ5LmRvY2tlci5pbyIsImV4cCI6MTU3NjA3NTA0MiwiaWF0IjoxNTc2MDc0NzQyLCJpc3MiOiJhdXRoLmRvY2tlci5pbyIsImp0aSI6Imh2R3g1YlYzTTBfYnNaaEVrcS1UIiwibmJmIjoxNTc2MDc0NDQyLCJzdWIiOiIifQ.HYzG_ir5aYQAZlXrgaxCOOw9sGFNwzuunyO_7qEUbxWkM8V_3PnZxQypAYU9cnS66n293A-N1LbYBHRmiKq87LHq8MAdjvhfeTnqC-d0GV5WJOksN98x_T-eIBmdpFuAykTH1ZBsUA6OzAvuQqJ6zHy3GwgBpbFXVI0EZQHUELruNEkv8sgjWQeqG_U3sGbhN2wxWG51w9Mvp-9WvM_o4EcbUnzvYKVjJVQI-LjvVvI0b39SfZyKJkXDMGf-Op4SFo8MZw6edTUXyoIijQP1d7_Zfv3_i5nv-exUX9Ju4QPixlpSi2JwE5_2SJxIhddPRiGq9s6n-bFIdTN4WB0Rbw","expires_in":300,"issued_at":"2019-12-11T14:32:22.931794853Z"} + headers: + Content-Type: + - application/json + Date: + - Wed, 11 Dec 2019 14:32:22 GMT + Strict-Transport-Security: + - max-age=31536000 + status: 200 OK + code: 200 + duration: "" +- request: + body: "" + form: {} + headers: + Accept: + - application/vnd.oci.image.manifest.v1+json + - application/vnd.docker.distribution.manifest.v2+json + - application/vnd.docker.distribution.manifest.v1+prettyjws + - application/vnd.docker.distribution.manifest.v1+json + - application/vnd.docker.distribution.manifest.list.v2+json + Authorization: + - Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsIng1YyI6WyJNSUlDK2pDQ0FwK2dBd0lCQWdJQkFEQUtCZ2dxaGtqT1BRUURBakJHTVVRd1FnWURWUVFERXpzeVYwNVpPbFZMUzFJNlJFMUVVanBTU1U5Rk9reEhOa0U2UTFWWVZEcE5SbFZNT2tZelNFVTZOVkF5VlRwTFNqTkdPa05CTmxrNlNrbEVVVEFlRncweE9UQXhNVEl3TURJeU5EVmFGdzB5TURBeE1USXdNREl5TkRWYU1FWXhSREJDQmdOVkJBTVRPMUpMTkZNNlMwRkxVVHBEV0RWRk9rRTJSMVE2VTBwTVR6cFFNbEpMT2tOWlZVUTZTMEpEU0RwWFNVeE1Pa3hUU2xrNldscFFVVHBaVWxsRU1JSUJJakFOQmdrcWhraUc5dzBCQVFFRkFBT0NBUThBTUlJQkNnS0NBUUVBcjY2bXkveXpHN21VUzF3eFQ3dFplS2pqRzcvNnBwZFNMY3JCcko5VytwcndzMGtIUDVwUHRkMUpkcFdEWU1OZWdqQXhpUWtRUUNvd25IUnN2ODVUalBUdE5wUkdKVTRkeHJkeXBvWGc4TVhYUEUzL2lRbHhPS2VNU0prNlRKbG5wNGFtWVBHQlhuQXRoQzJtTlR5ak1zdFh2ZmNWN3VFYWpRcnlOVUcyUVdXQ1k1Ujl0a2k5ZG54Z3dCSEF6bG8wTzJCczFmcm5JbmJxaCtic3ZSZ1FxU3BrMWhxYnhSU3AyRlNrL2tBL1gyeUFxZzJQSUJxWFFMaTVQQ3krWERYZElJczV6VG9ZbWJUK0pmbnZaMzRLcG5mSkpNalpIRW4xUVJtQldOZXJZcVdtNVhkQVhUMUJrQU9aditMNFVwSTk3NFZFZ2ppY1JINVdBeWV4b1BFclRRSURBUUFCbzRHeU1JR3ZNQTRHQTFVZER3RUIvd1FFQXdJSGdEQVBCZ05WSFNVRUNEQUdCZ1JWSFNVQU1FUUdBMVVkRGdROUJEdFNTelJUT2t0QlMxRTZRMWcxUlRwQk5rZFVPbE5LVEU4NlVESlNTenBEV1ZWRU9rdENRMGc2VjBsTVREcE1VMHBaT2xwYVVGRTZXVkpaUkRCR0JnTlZIU01FUHpBOWdEc3lWMDVaT2xWTFMxSTZSRTFFVWpwU1NVOUZPa3hITmtFNlExVllWRHBOUmxWTU9rWXpTRVU2TlZBeVZUcExTak5HT2tOQk5sazZTa2xFVVRBS0JnZ3Foa2pPUFFRREFnTkpBREJHQWlFQXFOSXEwMFdZTmM5Z2tDZGdSUzRSWUhtNTRZcDBTa05Rd2lyMm5hSWtGd3dDSVFEMjlYdUl5TmpTa1cvWmpQaFlWWFB6QW9TNFVkRXNvUUhyUVZHMDd1N3ZsUT09Il19.eyJhY2Nlc3MiOlt7InR5cGUiOiJyZXBvc2l0b3J5IiwibmFtZSI6ImxpYnJhcnkvYnVzeWJveCIsImFjdGlvbnMiOlsicHVsbCJdfV0sImF1ZCI6InJlZ2lzdHJ5LmRvY2tlci5pbyIsImV4cCI6MTU3NjA3NTA0MiwiaWF0IjoxNTc2MDc0NzQyLCJpc3MiOiJhdXRoLmRvY2tlci5pbyIsImp0aSI6Imh2R3g1YlYzTTBfYnNaaEVrcS1UIiwibmJmIjoxNTc2MDc0NDQyLCJzdWIiOiIifQ.HYzG_ir5aYQAZlXrgaxCOOw9sGFNwzuunyO_7qEUbxWkM8V_3PnZxQypAYU9cnS66n293A-N1LbYBHRmiKq87LHq8MAdjvhfeTnqC-d0GV5WJOksN98x_T-eIBmdpFuAykTH1ZBsUA6OzAvuQqJ6zHy3GwgBpbFXVI0EZQHUELruNEkv8sgjWQeqG_U3sGbhN2wxWG51w9Mvp-9WvM_o4EcbUnzvYKVjJVQI-LjvVvI0b39SfZyKJkXDMGf-Op4SFo8MZw6edTUXyoIijQP1d7_Zfv3_i5nv-exUX9Ju4QPixlpSi2JwE5_2SJxIhddPRiGq9s6n-bFIdTN4WB0Rbw + Docker-Distribution-Api-Version: + - registry/2.0 + url: https://registry-1.docker.io/v2/library/busybox/manifests/this-does-not-exist + method: GET + response: + body: | + {"errors":[{"code":"MANIFEST_UNKNOWN","message":"manifest unknown","detail":{"Tag":"this-does-not-exist"}}]} + headers: + Content-Length: + - "109" + Content-Type: + - application/json + Date: + - Wed, 11 Dec 2019 14:32:23 GMT + Docker-Distribution-Api-Version: + - registry/2.0 + Strict-Transport-Security: + - max-age=31536000 + status: 404 Not Found + code: 404 + duration: "" diff --git a/docker/fixtures/recording-GetManifest-success-0.yaml b/docker/fixtures/recording-GetManifest-success-0.yaml new file mode 100644 index 0000000000..ff13cd4916 --- /dev/null +++ b/docker/fixtures/recording-GetManifest-success-0.yaml @@ -0,0 +1,85 @@ +--- +version: 1 +interactions: +- request: + body: "" + form: {} + headers: + Docker-Distribution-Api-Version: + - registry/2.0 + url: https://registry-1.docker.io/v2/ + method: GET + response: + body: | + {"errors":[{"code":"UNAUTHORIZED","message":"authentication required","detail":null}]} + headers: + Content-Length: + - "87" + Content-Type: + - application/json + Date: + - Wed, 11 Dec 2019 14:32:21 GMT + Docker-Distribution-Api-Version: + - registry/2.0 + Strict-Transport-Security: + - max-age=31536000 + Www-Authenticate: + - Bearer realm="https://auth.docker.io/token",service="registry.docker.io" + status: 401 Unauthorized + code: 401 + duration: "" +- request: + body: "" + form: {} + headers: {} + url: https://auth.docker.io/token?scope=repository%3Alibrary%2Fbusybox%3Apull&service=registry.docker.io + method: GET + response: + body: | + {"token":"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsIng1YyI6WyJNSUlDK2pDQ0FwK2dBd0lCQWdJQkFEQUtCZ2dxaGtqT1BRUURBakJHTVVRd1FnWURWUVFERXpzeVYwNVpPbFZMUzFJNlJFMUVVanBTU1U5Rk9reEhOa0U2UTFWWVZEcE5SbFZNT2tZelNFVTZOVkF5VlRwTFNqTkdPa05CTmxrNlNrbEVVVEFlRncweE9UQXhNVEl3TURJeU5EVmFGdzB5TURBeE1USXdNREl5TkRWYU1FWXhSREJDQmdOVkJBTVRPMUpMTkZNNlMwRkxVVHBEV0RWRk9rRTJSMVE2VTBwTVR6cFFNbEpMT2tOWlZVUTZTMEpEU0RwWFNVeE1Pa3hUU2xrNldscFFVVHBaVWxsRU1JSUJJakFOQmdrcWhraUc5dzBCQVFFRkFBT0NBUThBTUlJQkNnS0NBUUVBcjY2bXkveXpHN21VUzF3eFQ3dFplS2pqRzcvNnBwZFNMY3JCcko5VytwcndzMGtIUDVwUHRkMUpkcFdEWU1OZWdqQXhpUWtRUUNvd25IUnN2ODVUalBUdE5wUkdKVTRkeHJkeXBvWGc4TVhYUEUzL2lRbHhPS2VNU0prNlRKbG5wNGFtWVBHQlhuQXRoQzJtTlR5ak1zdFh2ZmNWN3VFYWpRcnlOVUcyUVdXQ1k1Ujl0a2k5ZG54Z3dCSEF6bG8wTzJCczFmcm5JbmJxaCtic3ZSZ1FxU3BrMWhxYnhSU3AyRlNrL2tBL1gyeUFxZzJQSUJxWFFMaTVQQ3krWERYZElJczV6VG9ZbWJUK0pmbnZaMzRLcG5mSkpNalpIRW4xUVJtQldOZXJZcVdtNVhkQVhUMUJrQU9aditMNFVwSTk3NFZFZ2ppY1JINVdBeWV4b1BFclRRSURBUUFCbzRHeU1JR3ZNQTRHQTFVZER3RUIvd1FFQXdJSGdEQVBCZ05WSFNVRUNEQUdCZ1JWSFNVQU1FUUdBMVVkRGdROUJEdFNTelJUT2t0QlMxRTZRMWcxUlRwQk5rZFVPbE5LVEU4NlVESlNTenBEV1ZWRU9rdENRMGc2VjBsTVREcE1VMHBaT2xwYVVGRTZXVkpaUkRCR0JnTlZIU01FUHpBOWdEc3lWMDVaT2xWTFMxSTZSRTFFVWpwU1NVOUZPa3hITmtFNlExVllWRHBOUmxWTU9rWXpTRVU2TlZBeVZUcExTak5HT2tOQk5sazZTa2xFVVRBS0JnZ3Foa2pPUFFRREFnTkpBREJHQWlFQXFOSXEwMFdZTmM5Z2tDZGdSUzRSWUhtNTRZcDBTa05Rd2lyMm5hSWtGd3dDSVFEMjlYdUl5TmpTa1cvWmpQaFlWWFB6QW9TNFVkRXNvUUhyUVZHMDd1N3ZsUT09Il19.eyJhY2Nlc3MiOlt7InR5cGUiOiJyZXBvc2l0b3J5IiwibmFtZSI6ImxpYnJhcnkvYnVzeWJveCIsImFjdGlvbnMiOlsicHVsbCJdfV0sImF1ZCI6InJlZ2lzdHJ5LmRvY2tlci5pbyIsImV4cCI6MTU3NjA3NTA0MSwiaWF0IjoxNTc2MDc0NzQxLCJpc3MiOiJhdXRoLmRvY2tlci5pbyIsImp0aSI6IkU3RnhGT21OVVl2dDdjT0x4a3FaIiwibmJmIjoxNTc2MDc0NDQxLCJzdWIiOiIifQ.TukqLeND_lhGUpkPOYYWIg4oYGywaRJETxm1F6lnzENLKvxCSZz8L_J92sx6np--RsQ5BRuW2pgj4A2BH8WlRseMtyamVmUgp91-aG6VUM2fWV1Fw7Ayfg7R7Z_8BqwzuPqR0NthKxffuDdfIWs0j28p1JZWAMfLcm-V_z_qizTGjIXhf0G3BP_XskQFHDIvQOCxadIf7b84Uvq03ZaJuIviVkHQOmfnrMarZ81WWfqnmPF9lO1DgiIWRTLupL1l3nGV5saU5Ms4DBxZ9qM5lXgfcK_honc8JD2bbF1rjwzcSg4mJ24Vgc4e-2U_x4OaoXv_SMp6K-sTJPGyveLy8w","access_token":"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsIng1YyI6WyJNSUlDK2pDQ0FwK2dBd0lCQWdJQkFEQUtCZ2dxaGtqT1BRUURBakJHTVVRd1FnWURWUVFERXpzeVYwNVpPbFZMUzFJNlJFMUVVanBTU1U5Rk9reEhOa0U2UTFWWVZEcE5SbFZNT2tZelNFVTZOVkF5VlRwTFNqTkdPa05CTmxrNlNrbEVVVEFlRncweE9UQXhNVEl3TURJeU5EVmFGdzB5TURBeE1USXdNREl5TkRWYU1FWXhSREJDQmdOVkJBTVRPMUpMTkZNNlMwRkxVVHBEV0RWRk9rRTJSMVE2VTBwTVR6cFFNbEpMT2tOWlZVUTZTMEpEU0RwWFNVeE1Pa3hUU2xrNldscFFVVHBaVWxsRU1JSUJJakFOQmdrcWhraUc5dzBCQVFFRkFBT0NBUThBTUlJQkNnS0NBUUVBcjY2bXkveXpHN21VUzF3eFQ3dFplS2pqRzcvNnBwZFNMY3JCcko5VytwcndzMGtIUDVwUHRkMUpkcFdEWU1OZWdqQXhpUWtRUUNvd25IUnN2ODVUalBUdE5wUkdKVTRkeHJkeXBvWGc4TVhYUEUzL2lRbHhPS2VNU0prNlRKbG5wNGFtWVBHQlhuQXRoQzJtTlR5ak1zdFh2ZmNWN3VFYWpRcnlOVUcyUVdXQ1k1Ujl0a2k5ZG54Z3dCSEF6bG8wTzJCczFmcm5JbmJxaCtic3ZSZ1FxU3BrMWhxYnhSU3AyRlNrL2tBL1gyeUFxZzJQSUJxWFFMaTVQQ3krWERYZElJczV6VG9ZbWJUK0pmbnZaMzRLcG5mSkpNalpIRW4xUVJtQldOZXJZcVdtNVhkQVhUMUJrQU9aditMNFVwSTk3NFZFZ2ppY1JINVdBeWV4b1BFclRRSURBUUFCbzRHeU1JR3ZNQTRHQTFVZER3RUIvd1FFQXdJSGdEQVBCZ05WSFNVRUNEQUdCZ1JWSFNVQU1FUUdBMVVkRGdROUJEdFNTelJUT2t0QlMxRTZRMWcxUlRwQk5rZFVPbE5LVEU4NlVESlNTenBEV1ZWRU9rdENRMGc2VjBsTVREcE1VMHBaT2xwYVVGRTZXVkpaUkRCR0JnTlZIU01FUHpBOWdEc3lWMDVaT2xWTFMxSTZSRTFFVWpwU1NVOUZPa3hITmtFNlExVllWRHBOUmxWTU9rWXpTRVU2TlZBeVZUcExTak5HT2tOQk5sazZTa2xFVVRBS0JnZ3Foa2pPUFFRREFnTkpBREJHQWlFQXFOSXEwMFdZTmM5Z2tDZGdSUzRSWUhtNTRZcDBTa05Rd2lyMm5hSWtGd3dDSVFEMjlYdUl5TmpTa1cvWmpQaFlWWFB6QW9TNFVkRXNvUUhyUVZHMDd1N3ZsUT09Il19.eyJhY2Nlc3MiOlt7InR5cGUiOiJyZXBvc2l0b3J5IiwibmFtZSI6ImxpYnJhcnkvYnVzeWJveCIsImFjdGlvbnMiOlsicHVsbCJdfV0sImF1ZCI6InJlZ2lzdHJ5LmRvY2tlci5pbyIsImV4cCI6MTU3NjA3NTA0MSwiaWF0IjoxNTc2MDc0NzQxLCJpc3MiOiJhdXRoLmRvY2tlci5pbyIsImp0aSI6IkU3RnhGT21OVVl2dDdjT0x4a3FaIiwibmJmIjoxNTc2MDc0NDQxLCJzdWIiOiIifQ.TukqLeND_lhGUpkPOYYWIg4oYGywaRJETxm1F6lnzENLKvxCSZz8L_J92sx6np--RsQ5BRuW2pgj4A2BH8WlRseMtyamVmUgp91-aG6VUM2fWV1Fw7Ayfg7R7Z_8BqwzuPqR0NthKxffuDdfIWs0j28p1JZWAMfLcm-V_z_qizTGjIXhf0G3BP_XskQFHDIvQOCxadIf7b84Uvq03ZaJuIviVkHQOmfnrMarZ81WWfqnmPF9lO1DgiIWRTLupL1l3nGV5saU5Ms4DBxZ9qM5lXgfcK_honc8JD2bbF1rjwzcSg4mJ24Vgc4e-2U_x4OaoXv_SMp6K-sTJPGyveLy8w","expires_in":300,"issued_at":"2019-12-11T14:32:21.619376177Z"} + headers: + Content-Type: + - application/json + Date: + - Wed, 11 Dec 2019 14:32:21 GMT + Strict-Transport-Security: + - max-age=31536000 + status: 200 OK + code: 200 + duration: "" +- request: + body: "" + form: {} + headers: + Accept: + - application/vnd.oci.image.manifest.v1+json + - application/vnd.docker.distribution.manifest.v2+json + - application/vnd.docker.distribution.manifest.v1+prettyjws + - application/vnd.docker.distribution.manifest.v1+json + - application/vnd.docker.distribution.manifest.list.v2+json + Authorization: + - Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsIng1YyI6WyJNSUlDK2pDQ0FwK2dBd0lCQWdJQkFEQUtCZ2dxaGtqT1BRUURBakJHTVVRd1FnWURWUVFERXpzeVYwNVpPbFZMUzFJNlJFMUVVanBTU1U5Rk9reEhOa0U2UTFWWVZEcE5SbFZNT2tZelNFVTZOVkF5VlRwTFNqTkdPa05CTmxrNlNrbEVVVEFlRncweE9UQXhNVEl3TURJeU5EVmFGdzB5TURBeE1USXdNREl5TkRWYU1FWXhSREJDQmdOVkJBTVRPMUpMTkZNNlMwRkxVVHBEV0RWRk9rRTJSMVE2VTBwTVR6cFFNbEpMT2tOWlZVUTZTMEpEU0RwWFNVeE1Pa3hUU2xrNldscFFVVHBaVWxsRU1JSUJJakFOQmdrcWhraUc5dzBCQVFFRkFBT0NBUThBTUlJQkNnS0NBUUVBcjY2bXkveXpHN21VUzF3eFQ3dFplS2pqRzcvNnBwZFNMY3JCcko5VytwcndzMGtIUDVwUHRkMUpkcFdEWU1OZWdqQXhpUWtRUUNvd25IUnN2ODVUalBUdE5wUkdKVTRkeHJkeXBvWGc4TVhYUEUzL2lRbHhPS2VNU0prNlRKbG5wNGFtWVBHQlhuQXRoQzJtTlR5ak1zdFh2ZmNWN3VFYWpRcnlOVUcyUVdXQ1k1Ujl0a2k5ZG54Z3dCSEF6bG8wTzJCczFmcm5JbmJxaCtic3ZSZ1FxU3BrMWhxYnhSU3AyRlNrL2tBL1gyeUFxZzJQSUJxWFFMaTVQQ3krWERYZElJczV6VG9ZbWJUK0pmbnZaMzRLcG5mSkpNalpIRW4xUVJtQldOZXJZcVdtNVhkQVhUMUJrQU9aditMNFVwSTk3NFZFZ2ppY1JINVdBeWV4b1BFclRRSURBUUFCbzRHeU1JR3ZNQTRHQTFVZER3RUIvd1FFQXdJSGdEQVBCZ05WSFNVRUNEQUdCZ1JWSFNVQU1FUUdBMVVkRGdROUJEdFNTelJUT2t0QlMxRTZRMWcxUlRwQk5rZFVPbE5LVEU4NlVESlNTenBEV1ZWRU9rdENRMGc2VjBsTVREcE1VMHBaT2xwYVVGRTZXVkpaUkRCR0JnTlZIU01FUHpBOWdEc3lWMDVaT2xWTFMxSTZSRTFFVWpwU1NVOUZPa3hITmtFNlExVllWRHBOUmxWTU9rWXpTRVU2TlZBeVZUcExTak5HT2tOQk5sazZTa2xFVVRBS0JnZ3Foa2pPUFFRREFnTkpBREJHQWlFQXFOSXEwMFdZTmM5Z2tDZGdSUzRSWUhtNTRZcDBTa05Rd2lyMm5hSWtGd3dDSVFEMjlYdUl5TmpTa1cvWmpQaFlWWFB6QW9TNFVkRXNvUUhyUVZHMDd1N3ZsUT09Il19.eyJhY2Nlc3MiOlt7InR5cGUiOiJyZXBvc2l0b3J5IiwibmFtZSI6ImxpYnJhcnkvYnVzeWJveCIsImFjdGlvbnMiOlsicHVsbCJdfV0sImF1ZCI6InJlZ2lzdHJ5LmRvY2tlci5pbyIsImV4cCI6MTU3NjA3NTA0MSwiaWF0IjoxNTc2MDc0NzQxLCJpc3MiOiJhdXRoLmRvY2tlci5pbyIsImp0aSI6IkU3RnhGT21OVVl2dDdjT0x4a3FaIiwibmJmIjoxNTc2MDc0NDQxLCJzdWIiOiIifQ.TukqLeND_lhGUpkPOYYWIg4oYGywaRJETxm1F6lnzENLKvxCSZz8L_J92sx6np--RsQ5BRuW2pgj4A2BH8WlRseMtyamVmUgp91-aG6VUM2fWV1Fw7Ayfg7R7Z_8BqwzuPqR0NthKxffuDdfIWs0j28p1JZWAMfLcm-V_z_qizTGjIXhf0G3BP_XskQFHDIvQOCxadIf7b84Uvq03ZaJuIviVkHQOmfnrMarZ81WWfqnmPF9lO1DgiIWRTLupL1l3nGV5saU5Ms4DBxZ9qM5lXgfcK_honc8JD2bbF1rjwzcSg4mJ24Vgc4e-2U_x4OaoXv_SMp6K-sTJPGyveLy8w + Docker-Distribution-Api-Version: + - registry/2.0 + url: https://registry-1.docker.io/v2/library/busybox/manifests/latest + method: GET + response: + body: '{"manifests":[{"digest":"sha256:24fd20af232ca4ab5efbf1aeae7510252e2b60b15e9a78947467340607cd2ea2","mediaType":"application\/vnd.docker.distribution.manifest.v2+json","platform":{"architecture":"amd64","os":"linux"},"size":527},{"digest":"sha256:b19898c529964a48ce66923a06ddbde9cd5ae2472a42891b55cc00f7a60ba676","mediaType":"application\/vnd.docker.distribution.manifest.v2+json","platform":{"architecture":"arm","os":"linux","variant":"v5"},"size":527},{"digest":"sha256:7f0daf640bfd30871f64d8fe7d457931259b1c0aec5c929a8daabbe44c359337","mediaType":"application\/vnd.docker.distribution.manifest.v2+json","platform":{"architecture":"arm","os":"linux","variant":"v6"},"size":527},{"digest":"sha256:7044f6fc222ac87449d87e041eae6b5254012a8b4cbbc35e5b317ac61aa12557","mediaType":"application\/vnd.docker.distribution.manifest.v2+json","platform":{"architecture":"arm","os":"linux","variant":"v7"},"size":527},{"digest":"sha256:50edf1d080946c6a76989d1c3b0e753b62f7d9b5f5e66e88bef23ebbd1e9709c","mediaType":"application\/vnd.docker.distribution.manifest.v2+json","platform":{"architecture":"arm64","os":"linux","variant":"v8"},"size":527},{"digest":"sha256:cce548084ce402540134267ae6b98a9ce9273fcb57c801e07daeca7de2b2222b","mediaType":"application\/vnd.docker.distribution.manifest.v2+json","platform":{"architecture":"386","os":"linux"},"size":527},{"digest":"sha256:362ea5ff976609d3a811e5423bd8c11e1f031ae90ee823b1e56362dfe1d16d87","mediaType":"application\/vnd.docker.distribution.manifest.v2+json","platform":{"architecture":"ppc64le","os":"linux"},"size":528},{"digest":"sha256:59d479d844385faca8e6abce4df3ff3bcb54cc3ab6e7a51838c75f91451311b5","mediaType":"application\/vnd.docker.distribution.manifest.v2+json","platform":{"architecture":"s390x","os":"linux"},"size":528}],"mediaType":"application\/vnd.docker.distribution.manifest.list.v2+json","schemaVersion":2}' + headers: + Content-Length: + - "1864" + Content-Type: + - application/vnd.docker.distribution.manifest.list.v2+json + Date: + - Wed, 11 Dec 2019 14:32:22 GMT + Docker-Content-Digest: + - sha256:1828edd60c5efd34b2bf5dd3282ec0cc04d47b2ff9caa0b6d4f07a21d1c08084 + Docker-Distribution-Api-Version: + - registry/2.0 + Etag: + - '"sha256:1828edd60c5efd34b2bf5dd3282ec0cc04d47b2ff9caa0b6d4f07a21d1c08084"' + Strict-Transport-Security: + - max-age=31536000 + status: 200 OK + code: 200 + duration: "" diff --git a/docker/fixtures/recording-detectProperties-docker.io-0.yaml b/docker/fixtures/recording-detectProperties-docker.io-0.yaml new file mode 100644 index 0000000000..83fb87fc44 --- /dev/null +++ b/docker/fixtures/recording-detectProperties-docker.io-0.yaml @@ -0,0 +1,30 @@ +--- +version: 1 +rwmutex: {} +interactions: +- request: + body: "" + form: {} + headers: + Docker-Distribution-Api-Version: + - registry/2.0 + url: https://registry-1.docker.io/v2/ + method: GET + response: + body: | + {"errors":[{"code":"UNAUTHORIZED","message":"authentication required","detail":null}]} + headers: + Content-Length: + - "87" + Content-Type: + - application/json; charset=utf-8 + Date: + - Sat, 25 Mar 2017 06:50:52 GMT + Docker-Distribution-Api-Version: + - registry/2.0 + Strict-Transport-Security: + - max-age=31536000 + Www-Authenticate: + - Bearer realm="https://auth.docker.io/token",service="registry.docker.io" + status: 401 Unauthorized + code: 401 diff --git a/docker/fixtures/recording-detectProperties-openshift-0.yaml b/docker/fixtures/recording-detectProperties-openshift-0.yaml new file mode 100644 index 0000000000..3351685dab --- /dev/null +++ b/docker/fixtures/recording-detectProperties-openshift-0.yaml @@ -0,0 +1,30 @@ +--- +version: 1 +rwmutex: {} +interactions: +- request: + body: "" + form: {} + headers: + Docker-Distribution-Api-Version: + - registry/2.0 + url: http://localhost:5000/v2/ + method: GET + response: + body: | + {"errors":[{"code":"UNAUTHORIZED","message":"authentication required","detail":null}]} + headers: + Content-Length: + - "87" + Content-Type: + - application/json; charset=utf-8 + Date: + - Sat, 25 Mar 2017 06:55:11 GMT + Docker-Distribution-Api-Version: + - registry/2.0 + Www-Authenticate: + - Bearer realm="http://localhost:5000/openshift/token" + X-Registry-Supports-Signatures: + - "1" + status: 401 Unauthorized + code: 401 diff --git a/docker/fixtures/recording-getExtensionsSignatures-success-0.yaml b/docker/fixtures/recording-getExtensionsSignatures-success-0.yaml new file mode 100644 index 0000000000..fb81b803d2 --- /dev/null +++ b/docker/fixtures/recording-getExtensionsSignatures-success-0.yaml @@ -0,0 +1,77 @@ +--- +version: 1 +rwmutex: {} +interactions: +- request: + body: "" + form: {} + headers: + Docker-Distribution-Api-Version: + - registry/2.0 + url: http://localhost:5000/v2/ + method: GET + response: + body: | + {"errors":[{"code":"UNAUTHORIZED","message":"authentication required","detail":null}]} + headers: + Content-Length: + - "87" + Content-Type: + - application/json; charset=utf-8 + Date: + - Tue, 28 Mar 2017 17:36:28 GMT + Docker-Distribution-Api-Version: + - registry/2.0 + Www-Authenticate: + - Bearer realm="http://localhost:5000/openshift/token" + X-Registry-Supports-Signatures: + - "1" + status: 401 Unauthorized + code: 401 +- request: + body: "" + form: {} + headers: + Authorization: + - Basic dW51c2VkOmRoMmp1aHU2TGJHWUdTSEtNVWE1QkZFcHlvUE1ZRFZBNTloeGQzRkNmYlU= + url: http://localhost:5000/openshift/token?account=unused&scope=repository%3Amyns%2Fpersonal%3Apull + method: GET + response: + body: | + {"access_token":"dh2juhu6LbGYGSHKMUa5BFEpyoPMYDVA59hxd3FCfbU","token":"dh2juhu6LbGYGSHKMUa5BFEpyoPMYDVA59hxd3FCfbU"} + headers: + Content-Length: + - "117" + Content-Type: + - application/json + Date: + - Tue, 28 Mar 2017 17:36:28 GMT + Docker-Distribution-Api-Version: + - registry/2.0 + status: 200 OK + code: 200 +- request: + body: "" + form: {} + headers: + Authorization: + - Bearer dh2juhu6LbGYGSHKMUa5BFEpyoPMYDVA59hxd3FCfbU + Docker-Distribution-Api-Version: + - registry/2.0 + url: http://localhost:5000/extensions/v2/myns/personal/signatures/sha256:8d7fe3e157e56648ab790794970fbdfe82c84af79e807443b98df92c822a9b9b + method: GET + response: + body: '{"signatures":[{"schemaVersion":2,"name":"sha256:8d7fe3e157e56648ab790794970fbdfe82c84af79e807443b98df92c822a9b9b@809439d23da88df57186b0f2fce91e9a","type":"atomic","content":"owGbwMvMwMFY+D11/6GF988ynj6Ql8QQcWvenGql5KLMkszkxBwlq2qlzJTUvJLMkkoQOyU/OTu1SLcoNS21KDUvOVXJSiknH6guI7+4xMrUwMBAP7cyr1i/ILWoOD8vMccKxlCq1VHKzE1MT0UyJDcxLzMttbhENyUzHUgBjSrOSDQyNbOySDFPSzVONTQ1TzU1MzOxSEwytzQwtzSxNDdIS0pJS7UwSrYwSUwzt0y1MDA3MTFOsrRISbMEChoZJVomWSaBLCupLAA5LrEkPzczWSE5P68kMTMvtUihODM9L7GktCgVpCi/oCQT7DorkI9TgYqLEHoM9Az1DHRTUsuUgKZl5gJdmJhboGRlaAJ0i5GRial5bW0nowwLAyMHAxsrEyjYGLg4BWCB+WQx+/+Se5fLX926JXvhxIUzd4pnzZARCmzy8T6+xSHpN8fimU33Q/b4VAg6l8/1ZNN3DVX9m9Q292TttwyJ0wenR5yL4f79asHchhuVM4y55hw10xfNm3b2fZdoo4Ia74eWFYrnuOfr7dYwdl6kfnODiOqSO0IblZzUA0/fib7/yFh2Bqd5Uvb1+bLcR24c0Rd4LeW89mvNNtayRWs7mabKLXR10Zot1hOccOj+e0Mnn7mmjg4q+XyX3+69smBxXz6L2vuMx/Esp64k6i2oZPc/7NV3eTart3kVo945a757f0oCeQyiLvIdWujs0Hv7V8bKrduW3+YLjX5q1/zpse3yZNZTs4VnMUgZJmWtd9xp6gsA"}]}' + headers: + Content-Length: + - "947" + Content-Type: + - application/json; charset=utf-8 + Date: + - Tue, 28 Mar 2017 17:36:28 GMT + Docker-Distribution-Api-Version: + - registry/2.0 + X-Registry-Supports-Signatures: + - "1" + status: 200 OK + code: 200 diff --git a/docker/fixtures/recording-getSignaturesFromAPIExtension-success-0.yaml b/docker/fixtures/recording-getSignaturesFromAPIExtension-success-0.yaml new file mode 100644 index 0000000000..75b56bb7af --- /dev/null +++ b/docker/fixtures/recording-getSignaturesFromAPIExtension-success-0.yaml @@ -0,0 +1,150 @@ +--- +version: 1 +rwmutex: {} +interactions: +- request: + body: "" + form: {} + headers: + Docker-Distribution-Api-Version: + - registry/2.0 + url: http://localhost:5000/v2/ + method: GET + response: + body: | + {"errors":[{"code":"UNAUTHORIZED","message":"authentication required","detail":null}]} + headers: + Content-Length: + - "87" + Content-Type: + - application/json; charset=utf-8 + Date: + - Tue, 28 Mar 2017 17:37:49 GMT + Docker-Distribution-Api-Version: + - registry/2.0 + Www-Authenticate: + - Bearer realm="http://localhost:5000/openshift/token" + X-Registry-Supports-Signatures: + - "1" + status: 401 Unauthorized + code: 401 +- request: + body: "" + form: {} + headers: + Authorization: + - Basic dW51c2VkOmRoMmp1aHU2TGJHWUdTSEtNVWE1QkZFcHlvUE1ZRFZBNTloeGQzRkNmYlU= + url: http://localhost:5000/openshift/token?account=unused&scope=repository%3Amyns%2Fpersonal%3Apull + method: GET + response: + body: | + {"access_token":"dh2juhu6LbGYGSHKMUa5BFEpyoPMYDVA59hxd3FCfbU","token":"dh2juhu6LbGYGSHKMUa5BFEpyoPMYDVA59hxd3FCfbU"} + headers: + Content-Length: + - "117" + Content-Type: + - application/json + Date: + - Tue, 28 Mar 2017 17:37:49 GMT + Docker-Distribution-Api-Version: + - registry/2.0 + status: 200 OK + code: 200 +- request: + body: "" + form: {} + headers: + Accept: + - application/vnd.oci.image.manifest.v1+json + - application/vnd.docker.distribution.manifest.v2+json + - application/vnd.docker.distribution.manifest.v1+prettyjws + - application/vnd.docker.distribution.manifest.v1+json + - application/vnd.docker.distribution.manifest.list.v2+json + Authorization: + - Bearer dh2juhu6LbGYGSHKMUa5BFEpyoPMYDVA59hxd3FCfbU + Docker-Distribution-Api-Version: + - registry/2.0 + url: http://localhost:5000/v2/myns/personal/manifests/personal + method: GET + response: + body: |- + { + "schemaVersion": 1, + "name": "library/busybox", + "tag": "latest", + "architecture": "amd64", + "fsLayers": [ + { + "blobSum": "sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4" + }, + { + "blobSum": "sha256:7520415ce76232cdd62ecc345cea5ea44f5b6b144dc62351f2cd2b08382532a3" + } + ], + "history": [ + { + "v1Compatibility": "{\"architecture\":\"amd64\",\"config\":{\"Hostname\":\"1295ff10ed92\",\"Domainname\":\"\",\"User\":\"\",\"AttachStdin\":false,\"AttachStdout\":false,\"AttachStderr\":false,\"Tty\":false,\"OpenStdin\":false,\"StdinOnce\":false,\"Env\":[\"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin\"],\"Cmd\":[\"sh\"],\"Image\":\"sha256:0d7e86beb406ca2ff3418fa5db5e25dd6f60fe7265d68a9a141a2aed005b1ae7\",\"Volumes\":null,\"WorkingDir\":\"\",\"Entrypoint\":null,\"OnBuild\":null,\"Labels\":{}},\"container\":\"d12e9fb4928df60ac71b4b47d56b9b6aec383cccceb3b9275029959403ab4f73\",\"container_config\":{\"Hostname\":\"1295ff10ed92\",\"Domainname\":\"\",\"User\":\"\",\"AttachStdin\":false,\"AttachStdout\":false,\"AttachStderr\":false,\"Tty\":false,\"OpenStdin\":false,\"StdinOnce\":false,\"Env\":[\"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin\"],\"Cmd\":[\"/bin/sh\",\"-c\",\"#(nop) \",\"CMD [\\\"sh\\\"]\"],\"Image\":\"sha256:0d7e86beb406ca2ff3418fa5db5e25dd6f60fe7265d68a9a141a2aed005b1ae7\",\"Volumes\":null,\"WorkingDir\":\"\",\"Entrypoint\":null,\"OnBuild\":null,\"Labels\":{}},\"created\":\"2017-03-09T18:28:04.586987216Z\",\"docker_version\":\"1.12.6\",\"id\":\"d7f9d170aa714bf287eb994aada10a57f4fb1bf8fd748cbfa36a7ab9549190c1\",\"os\":\"linux\",\"parent\":\"d0fb090f7b4758531261295c34b956bc0b8a6663fb98605612d3eb47a02cd7f5\",\"throwaway\":true}" + }, + { + "v1Compatibility": "{\"id\":\"d0fb090f7b4758531261295c34b956bc0b8a6663fb98605612d3eb47a02cd7f5\",\"created\":\"2017-03-09T18:28:03.975884948Z\",\"container_config\":{\"Cmd\":[\"/bin/sh -c #(nop) ADD file:c9ecd8ff00c653fb652ad5a0a9215e1f467f0cd9933653b8a2e5e475b68597ab in / \"]}}" + } + ], + "signatures": [ + { + "header": { + "jwk": { + "crv": "P-256", + "kid": "PRYK:5GY2:JV5G:UGSN:25LN:DOUO:H7KL:XB3S:VK7G:EIKO:U2Q6:ILES", + "kty": "EC", + "x": "HCobvlWuN9KNWJF1IbxUkB1jFAMQmJyBZvj0pitxedo", + "y": "lk7x2F7fgHuTHdhnJQZhEZoK9L4RJRc_E1NyziEm0Cg" + }, + "alg": "ES256" + }, + "signature": "My_Ka7GUJYcB-d6p2WWmkWGyOQ0OikuuMpBI0hbPLJ8cLFEORpIVqzWoKlSUl0YzgQrKWnbW6DWIlSMLrxo4OA", + "protected": "eyJmb3JtYXRMZW5ndGgiOjIwOTYsImZvcm1hdFRhaWwiOiJDbjAiLCJ0aW1lIjoiMjAxNy0wMy0yOFQxNzozNzo0OVoifQ" + } + ] + } + headers: + Content-Length: + - "2743" + Content-Type: + - application/vnd.docker.distribution.manifest.v1+prettyjws + Date: + - Tue, 28 Mar 2017 17:37:49 GMT + Docker-Content-Digest: + - sha256:8d7fe3e157e56648ab790794970fbdfe82c84af79e807443b98df92c822a9b9b + Docker-Distribution-Api-Version: + - registry/2.0 + Etag: + - '"sha256:8d7fe3e157e56648ab790794970fbdfe82c84af79e807443b98df92c822a9b9b"' + X-Registry-Supports-Signatures: + - "1" + status: 200 OK + code: 200 +- request: + body: "" + form: {} + headers: + Authorization: + - Bearer dh2juhu6LbGYGSHKMUa5BFEpyoPMYDVA59hxd3FCfbU + Docker-Distribution-Api-Version: + - registry/2.0 + url: http://localhost:5000/extensions/v2/myns/personal/signatures/sha256:8d7fe3e157e56648ab790794970fbdfe82c84af79e807443b98df92c822a9b9b + method: GET + response: + body: '{"signatures":[{"schemaVersion":2,"name":"sha256:8d7fe3e157e56648ab790794970fbdfe82c84af79e807443b98df92c822a9b9b@809439d23da88df57186b0f2fce91e9a","type":"atomic","content":"owGbwMvMwMFY+D11/6GF988ynj6Ql8QQcWvenGql5KLMkszkxBwlq2qlzJTUvJLMkkoQOyU/OTu1SLcoNS21KDUvOVXJSiknH6guI7+4xMrUwMBAP7cyr1i/ILWoOD8vMccKxlCq1VHKzE1MT0UyJDcxLzMttbhENyUzHUgBjSrOSDQyNbOySDFPSzVONTQ1TzU1MzOxSEwytzQwtzSxNDdIS0pJS7UwSrYwSUwzt0y1MDA3MTFOsrRISbMEChoZJVomWSaBLCupLAA5LrEkPzczWSE5P68kMTMvtUihODM9L7GktCgVpCi/oCQT7DorkI9TgYqLEHoM9Az1DHRTUsuUgKZl5gJdmJhboGRlaAJ0i5GRial5bW0nowwLAyMHAxsrEyjYGLg4BWCB+WQx+/+Se5fLX926JXvhxIUzd4pnzZARCmzy8T6+xSHpN8fimU33Q/b4VAg6l8/1ZNN3DVX9m9Q292TttwyJ0wenR5yL4f79asHchhuVM4y55hw10xfNm3b2fZdoo4Ia74eWFYrnuOfr7dYwdl6kfnODiOqSO0IblZzUA0/fib7/yFh2Bqd5Uvb1+bLcR24c0Rd4LeW89mvNNtayRWs7mabKLXR10Zot1hOccOj+e0Mnn7mmjg4q+XyX3+69smBxXz6L2vuMx/Esp64k6i2oZPc/7NV3eTart3kVo945a757f0oCeQyiLvIdWujs0Hv7V8bKrduW3+YLjX5q1/zpse3yZNZTs4VnMUgZJmWtd9xp6gsA"}]}' + headers: + Content-Length: + - "947" + Content-Type: + - application/json; charset=utf-8 + Date: + - Tue, 28 Mar 2017 17:37:49 GMT + Docker-Distribution-Api-Version: + - registry/2.0 + X-Registry-Supports-Signatures: + - "1" + status: 200 OK + code: 200 diff --git a/docker/fixtures/recording-putSignaturesToAPIExtension-success-0.yaml b/docker/fixtures/recording-putSignaturesToAPIExtension-success-0.yaml new file mode 100644 index 0000000000..9bdec46fde --- /dev/null +++ b/docker/fixtures/recording-putSignaturesToAPIExtension-success-0.yaml @@ -0,0 +1,127 @@ +--- +version: 1 +rwmutex: {} +interactions: +- request: + body: "" + form: {} + headers: + Docker-Distribution-Api-Version: + - registry/2.0 + url: http://localhost:5000/v2/ + method: GET + response: + body: | + {"errors":[{"code":"UNAUTHORIZED","message":"authentication required","detail":null}]} + headers: + Content-Length: + - "87" + Content-Type: + - application/json; charset=utf-8 + Date: + - Tue, 28 Mar 2017 17:39:15 GMT + Docker-Distribution-Api-Version: + - registry/2.0 + Www-Authenticate: + - Bearer realm="http://localhost:5000/openshift/token" + X-Registry-Supports-Signatures: + - "1" + status: 401 Unauthorized + code: 401 +- request: + body: "" + form: {} + headers: + Authorization: + - Basic dW51c2VkOmRoMmp1aHU2TGJHWUdTSEtNVWE1QkZFcHlvUE1ZRFZBNTloeGQzRkNmYlU= + url: http://localhost:5000/openshift/token?account=unused&scope=repository%3Amyns%2Fpersonal%3Apull%2Cpush + method: GET + response: + body: | + {"access_token":"dh2juhu6LbGYGSHKMUa5BFEpyoPMYDVA59hxd3FCfbU","token":"dh2juhu6LbGYGSHKMUa5BFEpyoPMYDVA59hxd3FCfbU"} + headers: + Content-Length: + - "117" + Content-Type: + - application/json + Date: + - Tue, 28 Mar 2017 17:39:15 GMT + Docker-Distribution-Api-Version: + - registry/2.0 + status: 200 OK + code: 200 +- request: + body: "" + form: {} + headers: + Authorization: + - Bearer dh2juhu6LbGYGSHKMUa5BFEpyoPMYDVA59hxd3FCfbU + Docker-Distribution-Api-Version: + - registry/2.0 + url: http://localhost:5000/extensions/v2/myns/personal/signatures/sha256:8d7fe3e157e56648ab790794970fbdfe82c84af79e807443b98df92c822a9b9b + method: GET + response: + body: '{"signatures":[{"schemaVersion":2,"name":"sha256:8d7fe3e157e56648ab790794970fbdfe82c84af79e807443b98df92c822a9b9b@809439d23da88df57186b0f2fce91e9a","type":"atomic","content":"owGbwMvMwMFY+D11/6GF988ynj6Ql8QQcWvenGql5KLMkszkxBwlq2qlzJTUvJLMkkoQOyU/OTu1SLcoNS21KDUvOVXJSiknH6guI7+4xMrUwMBAP7cyr1i/ILWoOD8vMccKxlCq1VHKzE1MT0UyJDcxLzMttbhENyUzHUgBjSrOSDQyNbOySDFPSzVONTQ1TzU1MzOxSEwytzQwtzSxNDdIS0pJS7UwSrYwSUwzt0y1MDA3MTFOsrRISbMEChoZJVomWSaBLCupLAA5LrEkPzczWSE5P68kMTMvtUihODM9L7GktCgVpCi/oCQT7DorkI9TgYqLEHoM9Az1DHRTUsuUgKZl5gJdmJhboGRlaAJ0i5GRial5bW0nowwLAyMHAxsrEyjYGLg4BWCB+WQx+/+Se5fLX926JXvhxIUzd4pnzZARCmzy8T6+xSHpN8fimU33Q/b4VAg6l8/1ZNN3DVX9m9Q292TttwyJ0wenR5yL4f79asHchhuVM4y55hw10xfNm3b2fZdoo4Ia74eWFYrnuOfr7dYwdl6kfnODiOqSO0IblZzUA0/fib7/yFh2Bqd5Uvb1+bLcR24c0Rd4LeW89mvNNtayRWs7mabKLXR10Zot1hOccOj+e0Mnn7mmjg4q+XyX3+69smBxXz6L2vuMx/Esp64k6i2oZPc/7NV3eTart3kVo945a757f0oCeQyiLvIdWujs0Hv7V8bKrduW3+YLjX5q1/zpse3yZNZTs4VnMUgZJmWtd9xp6gsA"}]}' + headers: + Content-Length: + - "947" + Content-Type: + - application/json; charset=utf-8 + Date: + - Tue, 28 Mar 2017 17:39:15 GMT + Docker-Distribution-Api-Version: + - registry/2.0 + X-Registry-Supports-Signatures: + - "1" + status: 200 OK + code: 200 +- request: + body: '{"schemaVersion":2,"name":"sha256:8d7fe3e157e56648ab790794970fbdfe82c84af79e807443b98df92c822a9b9b@14e5f1b9f412d59d11fe971c649ff352","type":"atomic","content":"VGhpcyBpcyBub3QgcmVhbGx5IGEgc2lnbmF0dXJl"}' + form: {} + headers: + Authorization: + - Bearer dh2juhu6LbGYGSHKMUa5BFEpyoPMYDVA59hxd3FCfbU + Docker-Distribution-Api-Version: + - registry/2.0 + url: http://localhost:5000/extensions/v2/myns/personal/signatures/sha256:8d7fe3e157e56648ab790794970fbdfe82c84af79e807443b98df92c822a9b9b + method: PUT + response: + body: "" + headers: + Content-Length: + - "0" + Content-Type: + - text/plain; charset=utf-8 + Date: + - Tue, 28 Mar 2017 17:39:15 GMT + Docker-Distribution-Api-Version: + - registry/2.0 + X-Registry-Supports-Signatures: + - "1" + status: 201 Created + code: 201 +- request: + body: "" + form: {} + headers: + Authorization: + - Bearer dh2juhu6LbGYGSHKMUa5BFEpyoPMYDVA59hxd3FCfbU + Docker-Distribution-Api-Version: + - registry/2.0 + url: http://localhost:5000/extensions/v2/myns/personal/signatures/sha256:8d7fe3e157e56648ab790794970fbdfe82c84af79e807443b98df92c822a9b9b + method: GET + response: + body: '{"signatures":[{"schemaVersion":2,"name":"sha256:8d7fe3e157e56648ab790794970fbdfe82c84af79e807443b98df92c822a9b9b@809439d23da88df57186b0f2fce91e9a","type":"atomic","content":"owGbwMvMwMFY+D11/6GF988ynj6Ql8QQcWvenGql5KLMkszkxBwlq2qlzJTUvJLMkkoQOyU/OTu1SLcoNS21KDUvOVXJSiknH6guI7+4xMrUwMBAP7cyr1i/ILWoOD8vMccKxlCq1VHKzE1MT0UyJDcxLzMttbhENyUzHUgBjSrOSDQyNbOySDFPSzVONTQ1TzU1MzOxSEwytzQwtzSxNDdIS0pJS7UwSrYwSUwzt0y1MDA3MTFOsrRISbMEChoZJVomWSaBLCupLAA5LrEkPzczWSE5P68kMTMvtUihODM9L7GktCgVpCi/oCQT7DorkI9TgYqLEHoM9Az1DHRTUsuUgKZl5gJdmJhboGRlaAJ0i5GRial5bW0nowwLAyMHAxsrEyjYGLg4BWCB+WQx+/+Se5fLX926JXvhxIUzd4pnzZARCmzy8T6+xSHpN8fimU33Q/b4VAg6l8/1ZNN3DVX9m9Q292TttwyJ0wenR5yL4f79asHchhuVM4y55hw10xfNm3b2fZdoo4Ia74eWFYrnuOfr7dYwdl6kfnODiOqSO0IblZzUA0/fib7/yFh2Bqd5Uvb1+bLcR24c0Rd4LeW89mvNNtayRWs7mabKLXR10Zot1hOccOj+e0Mnn7mmjg4q+XyX3+69smBxXz6L2vuMx/Esp64k6i2oZPc/7NV3eTart3kVo945a757f0oCeQyiLvIdWujs0Hv7V8bKrduW3+YLjX5q1/zpse3yZNZTs4VnMUgZJmWtd9xp6gsA"},{"schemaVersion":2,"name":"sha256:8d7fe3e157e56648ab790794970fbdfe82c84af79e807443b98df92c822a9b9b@14e5f1b9f412d59d11fe971c649ff352","type":"atomic","content":"VGhpcyBpcyBub3QgcmVhbGx5IGEgc2lnbmF0dXJl"}]}' + headers: + Content-Length: + - "1150" + Content-Type: + - application/json; charset=utf-8 + Date: + - Tue, 28 Mar 2017 17:39:15 GMT + Docker-Distribution-Api-Version: + - registry/2.0 + X-Registry-Supports-Signatures: + - "1" + status: 200 OK + code: 200 diff --git a/go.mod b/go.mod index d345f27fe1..696f935a14 100644 --- a/go.mod +++ b/go.mod @@ -9,6 +9,7 @@ require ( github.com/containers/libtrust v0.0.0-20190913040956-14b96171aa3b github.com/containers/ocicrypt v1.0.3 github.com/containers/storage v1.24.5 + github.com/dnaeon/go-vcr v1.1.0 github.com/docker/distribution v2.7.1+incompatible github.com/docker/docker v1.4.2-0.20191219165747-a9416c67da9f github.com/docker/docker-credential-helpers v0.6.3 diff --git a/go.sum b/go.sum index 4043ae7566..49907b2f48 100644 --- a/go.sum +++ b/go.sum @@ -103,6 +103,10 @@ github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dnaeon/go-vcr v1.0.1 h1:r8L/HqC0Hje5AXMu1ooW8oyQyOFv4GxqpL0nRP7SLLY= +github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E= +github.com/dnaeon/go-vcr v1.1.0 h1:ReYa/UBrRyQdant9B4fNHGoCNKw6qh6P0fsdGmZpR7c= +github.com/dnaeon/go-vcr v1.1.0/go.mod h1:M7tiix8f0r6mKKJ3Yq/kqU1OYf3MnfmBWVbPx/yU9ko= github.com/docker/distribution v2.7.1+incompatible h1:a5mlkVzth6W5A4fOsS3D2EO5BUmsJpcB+cRlLU7cSug= github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/docker v1.4.2-0.20191219165747-a9416c67da9f h1:Sm8iD2lifO31DwXfkGzq8VgA7rwxPjRsYmeo0K/dF9Y= @@ -257,6 +261,8 @@ github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modocache/gover v0.0.0-20171022184752-b58185e213c5 h1:8Q0qkMVC/MmWkpIdlvZgcv2o2jrlF6zqVOh7W5YHdMA= +github.com/modocache/gover v0.0.0-20171022184752-b58185e213c5/go.mod h1:caMODM3PzxT8aQXRPkAt8xlV/e7d7w8GM5g0fa5F0D8= github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/mrunalp/fileutils v0.0.0-20171103030105-7d4729fb3618/go.mod h1:x8F1gnqOkIEiO4rqoeEEEqQbo7HjGMTvyoq3gej4iT0=