mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
vendor docker/distribution d06d6d3b093302c02a93153ac7b06ebc0ffd1793
- fix and add integration tests Signed-off-by: Antonio Murdaca <runcom@redhat.com>
This commit is contained in:
parent
ea0025a7e1
commit
c48439af7f
11 changed files with 94 additions and 61 deletions
|
@ -48,7 +48,7 @@ clone git github.com/boltdb/bolt v1.1.0
|
|||
clone git github.com/miekg/dns 75e6e86cc601825c5dbcd4e0c209eab180997cd7
|
||||
|
||||
# get graph and distribution packages
|
||||
clone git github.com/docker/distribution db17a23b961978730892e12a0c6051d43a31aab3
|
||||
clone git github.com/docker/distribution d06d6d3b093302c02a93153ac7b06ebc0ffd1793
|
||||
clone git github.com/vbatts/tar-split v0.9.11
|
||||
|
||||
# get desired notary commit, might also need to be updated in Dockerfile
|
||||
|
|
|
@ -549,32 +549,61 @@ func (s *DockerSuite) TestPushToCentralRegistryUnauthorized(c *check.C) {
|
|||
c.Assert(out, checker.Contains, "unauthorized: access to the requested resource is not authorized")
|
||||
}
|
||||
|
||||
func (s *DockerRegistryAuthTokenSuite) TestPushTokenServiceUnauthResponse(c *check.C) {
|
||||
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusUnauthorized)
|
||||
func getTestTokenService(status int, body string) *httptest.Server {
|
||||
return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(status)
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.Write([]byte(`{"errors": [{"Code":"UNAUTHORIZED", "message": "a message", "detail": null}]}`))
|
||||
w.Write([]byte(body))
|
||||
}))
|
||||
}
|
||||
|
||||
func (s *DockerRegistryAuthTokenSuite) TestPushTokenServiceUnauthResponse(c *check.C) {
|
||||
ts := getTestTokenService(http.StatusUnauthorized, `{"errors": [{"Code":"UNAUTHORIZED", "message": "a message", "detail": null}]}`)
|
||||
defer ts.Close()
|
||||
s.setupRegistryWithTokenService(c, ts.URL)
|
||||
repoName := fmt.Sprintf("%s/busybox", privateRegistryURL)
|
||||
dockerCmd(c, "tag", "busybox", repoName)
|
||||
out, _, err := dockerCmdWithError("push", repoName)
|
||||
c.Assert(err, check.NotNil, check.Commentf(out))
|
||||
c.Assert(out, checker.Not(checker.Contains), "Retrying")
|
||||
c.Assert(out, checker.Contains, "unauthorized: a message")
|
||||
}
|
||||
|
||||
func (s *DockerRegistryAuthTokenSuite) TestPushMisconfiguredTokenServiceResponse(c *check.C) {
|
||||
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusUnauthorized)
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
// this will make the daemon panics if no check is performed in retryOnError
|
||||
w.Write([]byte(`{"error": "unauthorized"}`))
|
||||
}))
|
||||
func (s *DockerRegistryAuthTokenSuite) TestPushMisconfiguredTokenServiceResponseUnauthorized(c *check.C) {
|
||||
ts := getTestTokenService(http.StatusUnauthorized, `{"error": "unauthorized"}`)
|
||||
defer ts.Close()
|
||||
s.setupRegistryWithTokenService(c, ts.URL)
|
||||
repoName := fmt.Sprintf("%s/busybox", privateRegistryURL)
|
||||
dockerCmd(c, "tag", "busybox", repoName)
|
||||
out, _, err := dockerCmdWithError("push", repoName)
|
||||
c.Assert(err, check.NotNil, check.Commentf(out))
|
||||
c.Assert(out, checker.Not(checker.Contains), "Retrying")
|
||||
split := strings.Split(out, "\n")
|
||||
c.Assert(split[len(split)-2], check.Equals, "unauthorized: authentication required")
|
||||
}
|
||||
|
||||
func (s *DockerRegistryAuthTokenSuite) TestPushMisconfiguredTokenServiceResponseError(c *check.C) {
|
||||
ts := getTestTokenService(http.StatusInternalServerError, `{"error": "unexpected"}`)
|
||||
defer ts.Close()
|
||||
s.setupRegistryWithTokenService(c, ts.URL)
|
||||
repoName := fmt.Sprintf("%s/busybox", privateRegistryURL)
|
||||
dockerCmd(c, "tag", "busybox", repoName)
|
||||
out, _, err := dockerCmdWithError("push", repoName)
|
||||
c.Assert(err, check.NotNil, check.Commentf(out))
|
||||
c.Assert(out, checker.Contains, "Retrying")
|
||||
split := strings.Split(out, "\n")
|
||||
c.Assert(split[len(split)-2], check.Equals, "received unexpected HTTP status: 500 Internal Server Error")
|
||||
}
|
||||
|
||||
func (s *DockerRegistryAuthTokenSuite) TestPushMisconfiguredTokenServiceResponseUnparsable(c *check.C) {
|
||||
ts := getTestTokenService(http.StatusForbidden, `no way`)
|
||||
defer ts.Close()
|
||||
s.setupRegistryWithTokenService(c, ts.URL)
|
||||
repoName := fmt.Sprintf("%s/busybox", privateRegistryURL)
|
||||
dockerCmd(c, "tag", "busybox", repoName)
|
||||
out, _, err := dockerCmdWithError("push", repoName)
|
||||
c.Assert(err, check.NotNil, check.Commentf(out))
|
||||
c.Assert(out, checker.Not(checker.Contains), "Retrying")
|
||||
split := strings.Split(out, "\n")
|
||||
c.Assert(split[len(split)-2], checker.Contains, "error parsing HTTP 403 response body: ")
|
||||
}
|
||||
|
|
|
@ -76,7 +76,7 @@ Some simple rules to ensure quick merge:
|
|||
You are heavily encouraged to first discuss what you want to do. You can do so on the irc channel, or by opening an issue that clearly describes the use case you want to fulfill, or the problem you are trying to solve.
|
||||
|
||||
If this is a major new feature, you should then submit a proposal that describes your technical solution and reasoning.
|
||||
If you did discuss it first, this will likely be greenlighted very fast. It's advisable to address all feedback on this proposal before starting actual work.
|
||||
If you did discuss it first, this will likely be greenlighted very fast. It's advisable to address all feedback on this proposal before starting actual work.
|
||||
|
||||
Then you should submit your implementation, clearly linking to the issue (and possible proposal).
|
||||
|
||||
|
@ -90,7 +90,7 @@ It's mandatory to:
|
|||
|
||||
Complying to these simple rules will greatly accelerate the review process, and will ensure you have a pleasant experience in contributing code to the Registry.
|
||||
|
||||
Have a look at a great, successful contribution: the [Ceph driver PR](https://github.com/docker/distribution/pull/443)
|
||||
Have a look at a great, successful contribution: the [Swift driver PR](https://github.com/docker/distribution/pull/493)
|
||||
|
||||
## Coding Style
|
||||
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
FROM golang:1.5.3
|
||||
|
||||
RUN apt-get update && \
|
||||
apt-get install -y librados-dev apache2-utils && \
|
||||
apt-get install -y apache2-utils && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
ENV DISTRIBUTION_DIR /go/src/github.com/docker/distribution
|
||||
ENV GOPATH $DISTRIBUTION_DIR/Godeps/_workspace:$GOPATH
|
||||
ENV DOCKER_BUILDTAGS include_rados include_oss include_gcs
|
||||
ENV DOCKER_BUILDTAGS include_oss include_gcs
|
||||
|
||||
WORKDIR $DISTRIBUTION_DIR
|
||||
COPY . $DISTRIBUTION_DIR
|
||||
|
|
|
@ -189,9 +189,11 @@ type BlobCreateOption interface {
|
|||
// BlobWriteService.Resume. If supported by the store, a writer can be
|
||||
// recovered with the id.
|
||||
type BlobWriter interface {
|
||||
io.WriteSeeker
|
||||
io.WriteCloser
|
||||
io.ReaderFrom
|
||||
io.Closer
|
||||
|
||||
// Size returns the number of bytes written to this blob.
|
||||
Size() int64
|
||||
|
||||
// ID returns the identifier for this writer. The ID can be used with the
|
||||
// Blob service to later resume the write.
|
||||
|
@ -216,9 +218,6 @@ type BlobWriter interface {
|
|||
// result in a no-op. This allows use of Cancel in a defer statement,
|
||||
// increasing the assurance that it is correctly called.
|
||||
Cancel(ctx context.Context) error
|
||||
|
||||
// Get a reader to the blob being written by this BlobWriter
|
||||
Reader() (io.ReadCloser, error)
|
||||
}
|
||||
|
||||
// BlobService combines the operations to access, read and write blobs. This
|
||||
|
|
|
@ -3,9 +3,6 @@ machine:
|
|||
pre:
|
||||
# Install gvm
|
||||
- bash < <(curl -s -S -L https://raw.githubusercontent.com/moovweb/gvm/1.0.22/binscripts/gvm-installer)
|
||||
# Install ceph to test rados driver & create pool
|
||||
- sudo -i ~/distribution/contrib/ceph/ci-setup.sh
|
||||
- ceph osd pool create docker-distribution 1
|
||||
# Install codecov for coverage
|
||||
- pip install --user codecov
|
||||
|
||||
|
@ -19,11 +16,9 @@ machine:
|
|||
BASE_DIR: src/github.com/$CIRCLE_PROJECT_USERNAME/$CIRCLE_PROJECT_REPONAME
|
||||
# Trick circle brainflat "no absolute path" behavior
|
||||
BASE_STABLE: ../../../$HOME/.gvm/pkgsets/stable/global/$BASE_DIR
|
||||
DOCKER_BUILDTAGS: "include_rados include_oss include_gcs"
|
||||
DOCKER_BUILDTAGS: "include_oss include_gcs"
|
||||
# Workaround Circle parsing dumb bugs and/or YAML wonkyness
|
||||
CIRCLE_PAIN: "mode: set"
|
||||
# Ceph config
|
||||
RADOS_POOL: "docker-distribution"
|
||||
|
||||
hosts:
|
||||
# Not used yet
|
||||
|
|
|
@ -25,7 +25,7 @@ type Challenge struct {
|
|||
type ChallengeManager interface {
|
||||
// GetChallenges returns the challenges for the given
|
||||
// endpoint URL.
|
||||
GetChallenges(endpoint string) ([]Challenge, error)
|
||||
GetChallenges(endpoint url.URL) ([]Challenge, error)
|
||||
|
||||
// AddResponse adds the response to the challenge
|
||||
// manager. The challenges will be parsed out of
|
||||
|
@ -48,8 +48,10 @@ func NewSimpleChallengeManager() ChallengeManager {
|
|||
|
||||
type simpleChallengeManager map[string][]Challenge
|
||||
|
||||
func (m simpleChallengeManager) GetChallenges(endpoint string) ([]Challenge, error) {
|
||||
challenges := m[endpoint]
|
||||
func (m simpleChallengeManager) GetChallenges(endpoint url.URL) ([]Challenge, error) {
|
||||
endpoint.Host = strings.ToLower(endpoint.Host)
|
||||
|
||||
challenges := m[endpoint.String()]
|
||||
return challenges, nil
|
||||
}
|
||||
|
||||
|
@ -60,11 +62,10 @@ func (m simpleChallengeManager) AddResponse(resp *http.Response) error {
|
|||
}
|
||||
urlCopy := url.URL{
|
||||
Path: resp.Request.URL.Path,
|
||||
Host: resp.Request.URL.Host,
|
||||
Host: strings.ToLower(resp.Request.URL.Host),
|
||||
Scheme: resp.Request.URL.Scheme,
|
||||
}
|
||||
m[urlCopy.String()] = challenges
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
@ -15,9 +15,15 @@ import (
|
|||
"github.com/docker/distribution/registry/client/transport"
|
||||
)
|
||||
|
||||
// ErrNoBasicAuthCredentials is returned if a request can't be authorized with
|
||||
// basic auth due to lack of credentials.
|
||||
var ErrNoBasicAuthCredentials = errors.New("no basic auth credentials")
|
||||
var (
|
||||
// ErrNoBasicAuthCredentials is returned if a request can't be authorized with
|
||||
// basic auth due to lack of credentials.
|
||||
ErrNoBasicAuthCredentials = errors.New("no basic auth credentials")
|
||||
|
||||
// ErrNoToken is returned if a request is successful but the body does not
|
||||
// contain an authorization token.
|
||||
ErrNoToken = errors.New("authorization server did not include a token in the response")
|
||||
)
|
||||
|
||||
const defaultClientID = "registry-client"
|
||||
|
||||
|
@ -77,9 +83,7 @@ func (ea *endpointAuthorizer) ModifyRequest(req *http.Request) error {
|
|||
Path: req.URL.Path[:v2Root+4],
|
||||
}
|
||||
|
||||
pingEndpoint := ping.String()
|
||||
|
||||
challenges, err := ea.challenges.GetChallenges(pingEndpoint)
|
||||
challenges, err := ea.challenges.GetChallenges(ping)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -404,7 +408,7 @@ func (th *tokenHandler) fetchTokenWithBasicAuth(realm *url.URL, service string,
|
|||
}
|
||||
|
||||
if tr.Token == "" {
|
||||
return "", time.Time{}, errors.New("authorization server did not include a token in the response")
|
||||
return "", time.Time{}, ErrNoToken
|
||||
}
|
||||
|
||||
if tr.ExpiresIn < minimumTokenLifetimeSeconds {
|
||||
|
|
|
@ -6,7 +6,6 @@ import (
|
|||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/docker/distribution"
|
||||
|
@ -104,21 +103,8 @@ func (hbu *httpBlobUpload) Write(p []byte) (n int, err error) {
|
|||
|
||||
}
|
||||
|
||||
func (hbu *httpBlobUpload) Seek(offset int64, whence int) (int64, error) {
|
||||
newOffset := hbu.offset
|
||||
|
||||
switch whence {
|
||||
case os.SEEK_CUR:
|
||||
newOffset += int64(offset)
|
||||
case os.SEEK_END:
|
||||
newOffset += int64(offset)
|
||||
case os.SEEK_SET:
|
||||
newOffset = int64(offset)
|
||||
}
|
||||
|
||||
hbu.offset = newOffset
|
||||
|
||||
return hbu.offset, nil
|
||||
func (hbu *httpBlobUpload) Size() int64 {
|
||||
return hbu.offset
|
||||
}
|
||||
|
||||
func (hbu *httpBlobUpload) ID() string {
|
||||
|
|
|
@ -2,6 +2,7 @@ package client
|
|||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
|
@ -10,6 +11,10 @@ import (
|
|||
"github.com/docker/distribution/registry/api/errcode"
|
||||
)
|
||||
|
||||
// ErrNoErrorsInBody is returned when a HTTP response body parses to an empty
|
||||
// errcode.Errors slice.
|
||||
var ErrNoErrorsInBody = errors.New("no error details found in HTTP response body")
|
||||
|
||||
// UnexpectedHTTPStatusError is returned when an unexpected HTTP status is
|
||||
// returned when making a registry api call.
|
||||
type UnexpectedHTTPStatusError struct {
|
||||
|
@ -17,18 +22,19 @@ type UnexpectedHTTPStatusError struct {
|
|||
}
|
||||
|
||||
func (e *UnexpectedHTTPStatusError) Error() string {
|
||||
return fmt.Sprintf("Received unexpected HTTP status: %s", e.Status)
|
||||
return fmt.Sprintf("received unexpected HTTP status: %s", e.Status)
|
||||
}
|
||||
|
||||
// UnexpectedHTTPResponseError is returned when an expected HTTP status code
|
||||
// is returned, but the content was unexpected and failed to be parsed.
|
||||
type UnexpectedHTTPResponseError struct {
|
||||
ParseErr error
|
||||
Response []byte
|
||||
ParseErr error
|
||||
StatusCode int
|
||||
Response []byte
|
||||
}
|
||||
|
||||
func (e *UnexpectedHTTPResponseError) Error() string {
|
||||
return fmt.Sprintf("Error parsing HTTP response: %s: %q", e.ParseErr.Error(), string(e.Response))
|
||||
return fmt.Sprintf("error parsing HTTP %d response body: %s: %q", e.StatusCode, e.ParseErr.Error(), string(e.Response))
|
||||
}
|
||||
|
||||
func parseHTTPErrorResponse(statusCode int, r io.Reader) error {
|
||||
|
@ -53,10 +59,22 @@ func parseHTTPErrorResponse(statusCode int, r io.Reader) error {
|
|||
|
||||
if err := json.Unmarshal(body, &errors); err != nil {
|
||||
return &UnexpectedHTTPResponseError{
|
||||
ParseErr: err,
|
||||
Response: body,
|
||||
ParseErr: err,
|
||||
StatusCode: statusCode,
|
||||
Response: body,
|
||||
}
|
||||
}
|
||||
|
||||
if len(errors) == 0 {
|
||||
// If there was no error specified in the body, return
|
||||
// UnexpectedHTTPResponseError.
|
||||
return &UnexpectedHTTPResponseError{
|
||||
ParseErr: ErrNoErrorsInBody,
|
||||
StatusCode: statusCode,
|
||||
Response: body,
|
||||
}
|
||||
}
|
||||
|
||||
return errors
|
||||
}
|
||||
|
||||
|
|
|
@ -308,6 +308,7 @@ check:
|
|||
if err != nil {
|
||||
return distribution.Descriptor{}, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
switch {
|
||||
case resp.StatusCode >= 200 && resp.StatusCode < 400:
|
||||
|
|
Loading…
Reference in a new issue