mirror of
https://github.com/fnproject/fn.git
synced 2022-10-28 21:29:17 +03:00
Updated deps
This commit is contained in:
@@ -3,6 +3,8 @@ package datastoreutil
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/jmoiron/sqlx"
|
||||
|
||||
"gitlab-odx.oracle.com/odx/functions/api/models"
|
||||
)
|
||||
|
||||
@@ -135,3 +137,12 @@ func (v *validator) GetTask(ctx context.Context, callID string) (*models.FnCall,
|
||||
}
|
||||
return v.Datastore.GetTask(ctx, callID)
|
||||
}
|
||||
|
||||
func (v *validator) DeleteLog(ctx context.Context, callID string) error {
|
||||
return v.Datastore.DeleteLog(ctx, callID)
|
||||
}
|
||||
|
||||
// GetDatabase returns the underlying sqlx database implementation
|
||||
func (v *validator) GetDatabase() *sqlx.DB {
|
||||
return v.Datastore.GetDatabase()
|
||||
}
|
||||
|
||||
@@ -3,6 +3,8 @@ package datastore
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/jmoiron/sqlx"
|
||||
|
||||
"gitlab-odx.oracle.com/odx/functions/api/datastore/internal/datastoreutil"
|
||||
"gitlab-odx.oracle.com/odx/functions/api/logs"
|
||||
"gitlab-odx.oracle.com/odx/functions/api/models"
|
||||
@@ -167,3 +169,8 @@ func (m *mock) GetTask(ctx context.Context, callID string) (*models.FnCall, erro
|
||||
func (m *mock) GetTasks(ctx context.Context, filter *models.CallFilter) (models.FnCalls, error) {
|
||||
return m.Calls, nil
|
||||
}
|
||||
|
||||
// GetDatabase returns nil here since shouldn't really be used
|
||||
func (m *mock) GetDatabase() *sqlx.DB {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -757,3 +757,8 @@ func scanCall(scanner RowScanner, call *models.FnCall) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetDatabase returns the underlying sqlx database implementation
|
||||
func (ds *sqlStore) GetDatabase() *sqlx.DB {
|
||||
return ds.db
|
||||
}
|
||||
|
||||
@@ -3,6 +3,8 @@ package models
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"github.com/jmoiron/sqlx"
|
||||
)
|
||||
|
||||
type Datastore interface {
|
||||
@@ -64,6 +66,9 @@ type Datastore interface {
|
||||
|
||||
// Implement FnLog methods for convenience
|
||||
FnLog
|
||||
|
||||
// GetDatabase returns the underlying sqlx database implementation
|
||||
GetDatabase() *sqlx.DB
|
||||
}
|
||||
|
||||
var (
|
||||
|
||||
2
glide.lock
generated
2
glide.lock
generated
@@ -40,7 +40,7 @@ imports:
|
||||
subpackages:
|
||||
- cli/config/configfile
|
||||
- name: github.com/docker/distribution
|
||||
version: a25b9ef0c9fe242ac04bb20d3a028442b7d266b6
|
||||
version: f86db6b22663a27ba4d278220b7e34be528b1e79
|
||||
subpackages:
|
||||
- context
|
||||
- digest
|
||||
|
||||
@@ -28,9 +28,7 @@ import:
|
||||
subpackages:
|
||||
- cli/config/configfile
|
||||
- package: github.com/docker/distribution
|
||||
version: ^2.6.1
|
||||
subpackages:
|
||||
- manifest/schema1
|
||||
branch: master
|
||||
- package: github.com/fsouza/go-dockerclient
|
||||
- package: github.com/garyburd/redigo
|
||||
subpackages:
|
||||
|
||||
8
vendor/github.com/docker/distribution/BUILDING.md
generated
vendored
8
vendor/github.com/docker/distribution/BUILDING.md
generated
vendored
@@ -71,9 +71,7 @@ commands, such as `go test`, should work per package (please see
|
||||
A `Makefile` has been provided as a convenience to support repeatable builds.
|
||||
Please install the following into `GOPATH` for it to work:
|
||||
|
||||
go get github.com/tools/godep github.com/golang/lint/golint
|
||||
|
||||
**TODO(stevvooe):** Add a `make setup` command to Makefile to run this. Have to think about how to interact with Godeps properly.
|
||||
go get github.com/golang/lint/golint
|
||||
|
||||
Once these commands are available in the `GOPATH`, run `make` to get a full
|
||||
build:
|
||||
@@ -105,8 +103,8 @@ build:
|
||||
+ /Users/sday/go/src/github.com/docker/distribution/bin/registry-api-descriptor-template
|
||||
+ binaries
|
||||
|
||||
The above provides a repeatable build using the contents of the vendored
|
||||
Godeps directory. This includes formatting, vetting, linting, building,
|
||||
The above provides a repeatable build using the contents of the vendor
|
||||
directory. This includes formatting, vetting, linting, building,
|
||||
testing and generating tagged binaries. We can verify this worked by running
|
||||
the registry binary generated in the "./bin" directory:
|
||||
|
||||
|
||||
6
vendor/github.com/docker/distribution/CHANGELOG.md
generated
vendored
6
vendor/github.com/docker/distribution/CHANGELOG.md
generated
vendored
@@ -1,11 +1,5 @@
|
||||
# Changelog
|
||||
|
||||
## 2.6.1 (2017-04-05)
|
||||
|
||||
#### Registry
|
||||
- Fix `Forwarded` header handling, revert use of `X-Forwarded-Port`
|
||||
- Use driver `Stat` for registry health check
|
||||
|
||||
## 2.6.0 (2017-01-18)
|
||||
|
||||
#### Storage
|
||||
|
||||
5
vendor/github.com/docker/distribution/Dockerfile
generated
vendored
5
vendor/github.com/docker/distribution/Dockerfile
generated
vendored
@@ -1,8 +1,11 @@
|
||||
FROM golang:1.7-alpine
|
||||
FROM golang:1.8-alpine
|
||||
|
||||
ENV DISTRIBUTION_DIR /go/src/github.com/docker/distribution
|
||||
ENV DOCKER_BUILDTAGS include_oss include_gcs
|
||||
|
||||
ARG GOOS=linux
|
||||
ARG GOARCH=amd64
|
||||
|
||||
RUN set -ex \
|
||||
&& apk add --no-cache make git
|
||||
|
||||
|
||||
458
vendor/github.com/docker/distribution/Godeps/Godeps.json
generated
vendored
458
vendor/github.com/docker/distribution/Godeps/Godeps.json
generated
vendored
@@ -1,458 +0,0 @@
|
||||
{
|
||||
"ImportPath": "github.com/docker/distribution",
|
||||
"GoVersion": "go1.6",
|
||||
"GodepVersion": "v74",
|
||||
"Packages": [
|
||||
"./..."
|
||||
],
|
||||
"Deps": [
|
||||
{
|
||||
"ImportPath": "github.com/Azure/azure-sdk-for-go/storage",
|
||||
"Comment": "v5.0.0-beta-6-g0b5fe2a",
|
||||
"Rev": "0b5fe2abe0271ba07049eacaa65922d67c319543"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/Sirupsen/logrus",
|
||||
"Comment": "v0.7.3",
|
||||
"Rev": "55eb11d21d2a31a3cc93838241d04800f52e823d"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/Sirupsen/logrus/formatters/logstash",
|
||||
"Comment": "v0.7.3",
|
||||
"Rev": "55eb11d21d2a31a3cc93838241d04800f52e823d"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/aws/aws-sdk-go/aws",
|
||||
"Comment": "v1.2.4",
|
||||
"Rev": "90dec2183a5f5458ee79cbaf4b8e9ab910bc81a6"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/aws/aws-sdk-go/aws/awserr",
|
||||
"Comment": "v1.2.4",
|
||||
"Rev": "90dec2183a5f5458ee79cbaf4b8e9ab910bc81a6"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/aws/aws-sdk-go/aws/awsutil",
|
||||
"Comment": "v1.2.4",
|
||||
"Rev": "90dec2183a5f5458ee79cbaf4b8e9ab910bc81a6"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/aws/aws-sdk-go/aws/client",
|
||||
"Comment": "v1.2.4",
|
||||
"Rev": "90dec2183a5f5458ee79cbaf4b8e9ab910bc81a6"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/aws/aws-sdk-go/aws/client/metadata",
|
||||
"Comment": "v1.2.4",
|
||||
"Rev": "90dec2183a5f5458ee79cbaf4b8e9ab910bc81a6"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/aws/aws-sdk-go/aws/corehandlers",
|
||||
"Comment": "v1.2.4",
|
||||
"Rev": "90dec2183a5f5458ee79cbaf4b8e9ab910bc81a6"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/aws/aws-sdk-go/aws/credentials",
|
||||
"Comment": "v1.2.4",
|
||||
"Rev": "90dec2183a5f5458ee79cbaf4b8e9ab910bc81a6"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/aws/aws-sdk-go/aws/credentials/ec2rolecreds",
|
||||
"Comment": "v1.2.4",
|
||||
"Rev": "90dec2183a5f5458ee79cbaf4b8e9ab910bc81a6"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/aws/aws-sdk-go/aws/defaults",
|
||||
"Comment": "v1.2.4",
|
||||
"Rev": "90dec2183a5f5458ee79cbaf4b8e9ab910bc81a6"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/aws/aws-sdk-go/aws/ec2metadata",
|
||||
"Comment": "v1.2.4",
|
||||
"Rev": "90dec2183a5f5458ee79cbaf4b8e9ab910bc81a6"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/aws/aws-sdk-go/aws/request",
|
||||
"Comment": "v1.2.4",
|
||||
"Rev": "90dec2183a5f5458ee79cbaf4b8e9ab910bc81a6"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/aws/aws-sdk-go/aws/session",
|
||||
"Comment": "v1.2.4",
|
||||
"Rev": "90dec2183a5f5458ee79cbaf4b8e9ab910bc81a6"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/aws/aws-sdk-go/aws/signer/v4",
|
||||
"Comment": "v1.2.4",
|
||||
"Rev": "90dec2183a5f5458ee79cbaf4b8e9ab910bc81a6"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/aws/aws-sdk-go/private/endpoints",
|
||||
"Comment": "v1.2.4",
|
||||
"Rev": "90dec2183a5f5458ee79cbaf4b8e9ab910bc81a6"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/aws/aws-sdk-go/private/protocol",
|
||||
"Comment": "v1.2.4",
|
||||
"Rev": "90dec2183a5f5458ee79cbaf4b8e9ab910bc81a6"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/aws/aws-sdk-go/private/protocol/query",
|
||||
"Comment": "v1.2.4",
|
||||
"Rev": "90dec2183a5f5458ee79cbaf4b8e9ab910bc81a6"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/aws/aws-sdk-go/private/protocol/query/queryutil",
|
||||
"Comment": "v1.2.4",
|
||||
"Rev": "90dec2183a5f5458ee79cbaf4b8e9ab910bc81a6"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/aws/aws-sdk-go/private/protocol/rest",
|
||||
"Comment": "v1.2.4",
|
||||
"Rev": "90dec2183a5f5458ee79cbaf4b8e9ab910bc81a6"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/aws/aws-sdk-go/private/protocol/restxml",
|
||||
"Comment": "v1.2.4",
|
||||
"Rev": "90dec2183a5f5458ee79cbaf4b8e9ab910bc81a6"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/aws/aws-sdk-go/private/protocol/xml/xmlutil",
|
||||
"Comment": "v1.2.4",
|
||||
"Rev": "90dec2183a5f5458ee79cbaf4b8e9ab910bc81a6"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/aws/aws-sdk-go/private/waiter",
|
||||
"Comment": "v1.2.4",
|
||||
"Rev": "90dec2183a5f5458ee79cbaf4b8e9ab910bc81a6"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/aws/aws-sdk-go/service/cloudfront/sign",
|
||||
"Comment": "v1.2.4",
|
||||
"Rev": "90dec2183a5f5458ee79cbaf4b8e9ab910bc81a6"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/aws/aws-sdk-go/service/s3",
|
||||
"Comment": "v1.2.4",
|
||||
"Rev": "90dec2183a5f5458ee79cbaf4b8e9ab910bc81a6"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/aws/aws-sdk-go/vendor/github.com/go-ini/ini",
|
||||
"Comment": "v1.2.4",
|
||||
"Rev": "90dec2183a5f5458ee79cbaf4b8e9ab910bc81a6"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/aws/aws-sdk-go/vendor/github.com/jmespath/go-jmespath",
|
||||
"Comment": "v1.2.4",
|
||||
"Rev": "90dec2183a5f5458ee79cbaf4b8e9ab910bc81a6"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/bugsnag/bugsnag-go",
|
||||
"Comment": "v1.0.2-5-gb1d1530",
|
||||
"Rev": "b1d153021fcd90ca3f080db36bec96dc690fb274"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/bugsnag/bugsnag-go/errors",
|
||||
"Comment": "v1.0.2-5-gb1d1530",
|
||||
"Rev": "b1d153021fcd90ca3f080db36bec96dc690fb274"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/bugsnag/osext",
|
||||
"Rev": "0dd3f918b21bec95ace9dc86c7e70266cfc5c702"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/bugsnag/panicwrap",
|
||||
"Comment": "1.0.0-2-ge2c2850",
|
||||
"Rev": "e2c28503fcd0675329da73bf48b33404db873782"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/denverdino/aliyungo/common",
|
||||
"Rev": "afedced274aa9a7fcdd47ac97018f0f8db4e5de2"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/denverdino/aliyungo/oss",
|
||||
"Rev": "afedced274aa9a7fcdd47ac97018f0f8db4e5de2"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/denverdino/aliyungo/util",
|
||||
"Rev": "afedced274aa9a7fcdd47ac97018f0f8db4e5de2"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/docker/goamz/aws",
|
||||
"Rev": "f0a21f5b2e12f83a505ecf79b633bb2035cf6f85"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/docker/goamz/s3",
|
||||
"Rev": "f0a21f5b2e12f83a505ecf79b633bb2035cf6f85"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/docker/libtrust",
|
||||
"Rev": "fa567046d9b14f6aa788882a950d69651d230b21"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/garyburd/redigo/internal",
|
||||
"Rev": "535138d7bcd717d6531c701ef5933d98b1866257"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/garyburd/redigo/redis",
|
||||
"Rev": "535138d7bcd717d6531c701ef5933d98b1866257"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/golang/protobuf/proto",
|
||||
"Rev": "8d92cf5fc15a4382f8964b08e1f42a75c0591aa3"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/gorilla/context",
|
||||
"Rev": "14f550f51af52180c2eefed15e5fd18d63c0a64a"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/gorilla/handlers",
|
||||
"Rev": "60c7bfde3e33c201519a200a4507a158cc03a17b"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/gorilla/mux",
|
||||
"Rev": "e444e69cbd2e2e3e0749a2f3c717cec491552bbf"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/inconshreveable/mousetrap",
|
||||
"Rev": "76626ae9c91c4f2a10f34cad8ce83ea42c93bb75"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/mitchellh/mapstructure",
|
||||
"Rev": "482a9fd5fa83e8c4e7817413b80f3eb8feec03ef"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/ncw/swift",
|
||||
"Rev": "ce444d6d47c51d4dda9202cd38f5094dd8e27e86"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/ncw/swift/swifttest",
|
||||
"Rev": "ce444d6d47c51d4dda9202cd38f5094dd8e27e86"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/spf13/cobra",
|
||||
"Rev": "312092086bed4968099259622145a0c9ae280064"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/spf13/pflag",
|
||||
"Rev": "5644820622454e71517561946e3d94b9f9db6842"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/stevvooe/resumable",
|
||||
"Rev": "51ad44105773cafcbe91927f70ac68e1bf78f8b4"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/stevvooe/resumable/sha256",
|
||||
"Rev": "51ad44105773cafcbe91927f70ac68e1bf78f8b4"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/stevvooe/resumable/sha512",
|
||||
"Rev": "51ad44105773cafcbe91927f70ac68e1bf78f8b4"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/yvasiyarov/go-metrics",
|
||||
"Rev": "57bccd1ccd43f94bb17fdd8bf3007059b802f85e"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/yvasiyarov/gorelic",
|
||||
"Comment": "v0.0.6-8-ga9bba5b",
|
||||
"Rev": "a9bba5b9ab508a086f9a12b8c51fab68478e2128"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/yvasiyarov/newrelic_platform_go",
|
||||
"Rev": "b21fdbd4370f3717f3bbd2bf41c223bc273068e6"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/crypto/bcrypt",
|
||||
"Rev": "c10c31b5e94b6f7a0283272dc2bb27163dcea24b"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/crypto/blowfish",
|
||||
"Rev": "c10c31b5e94b6f7a0283272dc2bb27163dcea24b"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/crypto/ocsp",
|
||||
"Rev": "c10c31b5e94b6f7a0283272dc2bb27163dcea24b"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/context",
|
||||
"Rev": "4876518f9e71663000c348837735820161a42df7"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/context/ctxhttp",
|
||||
"Rev": "4876518f9e71663000c348837735820161a42df7"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/http2",
|
||||
"Rev": "4876518f9e71663000c348837735820161a42df7"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/http2/hpack",
|
||||
"Rev": "4876518f9e71663000c348837735820161a42df7"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/internal/timeseries",
|
||||
"Rev": "4876518f9e71663000c348837735820161a42df7"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/trace",
|
||||
"Rev": "4876518f9e71663000c348837735820161a42df7"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/oauth2",
|
||||
"Rev": "045497edb6234273d67dbc25da3f2ddbc4c4cacf"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/oauth2/google",
|
||||
"Rev": "045497edb6234273d67dbc25da3f2ddbc4c4cacf"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/oauth2/internal",
|
||||
"Rev": "045497edb6234273d67dbc25da3f2ddbc4c4cacf"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/oauth2/jws",
|
||||
"Rev": "045497edb6234273d67dbc25da3f2ddbc4c4cacf"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/oauth2/jwt",
|
||||
"Rev": "045497edb6234273d67dbc25da3f2ddbc4c4cacf"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/time/rate",
|
||||
"Rev": "a4bde12657593d5e90d0533a3e4fd95e635124cb"
|
||||
},
|
||||
{
|
||||
"ImportPath": "google.golang.org/api/gensupport",
|
||||
"Rev": "9bf6e6e569ff057f75d9604a46c52928f17d2b54"
|
||||
},
|
||||
{
|
||||
"ImportPath": "google.golang.org/api/googleapi",
|
||||
"Rev": "9bf6e6e569ff057f75d9604a46c52928f17d2b54"
|
||||
},
|
||||
{
|
||||
"ImportPath": "google.golang.org/api/googleapi/internal/uritemplates",
|
||||
"Rev": "9bf6e6e569ff057f75d9604a46c52928f17d2b54"
|
||||
},
|
||||
{
|
||||
"ImportPath": "google.golang.org/api/storage/v1",
|
||||
"Rev": "9bf6e6e569ff057f75d9604a46c52928f17d2b54"
|
||||
},
|
||||
{
|
||||
"ImportPath": "google.golang.org/appengine",
|
||||
"Rev": "12d5545dc1cfa6047a286d5e853841b6471f4c19"
|
||||
},
|
||||
{
|
||||
"ImportPath": "google.golang.org/appengine/internal",
|
||||
"Rev": "12d5545dc1cfa6047a286d5e853841b6471f4c19"
|
||||
},
|
||||
{
|
||||
"ImportPath": "google.golang.org/appengine/internal/app_identity",
|
||||
"Rev": "12d5545dc1cfa6047a286d5e853841b6471f4c19"
|
||||
},
|
||||
{
|
||||
"ImportPath": "google.golang.org/appengine/internal/base",
|
||||
"Rev": "12d5545dc1cfa6047a286d5e853841b6471f4c19"
|
||||
},
|
||||
{
|
||||
"ImportPath": "google.golang.org/appengine/internal/datastore",
|
||||
"Rev": "12d5545dc1cfa6047a286d5e853841b6471f4c19"
|
||||
},
|
||||
{
|
||||
"ImportPath": "google.golang.org/appengine/internal/log",
|
||||
"Rev": "12d5545dc1cfa6047a286d5e853841b6471f4c19"
|
||||
},
|
||||
{
|
||||
"ImportPath": "google.golang.org/appengine/internal/modules",
|
||||
"Rev": "12d5545dc1cfa6047a286d5e853841b6471f4c19"
|
||||
},
|
||||
{
|
||||
"ImportPath": "google.golang.org/appengine/internal/remote_api",
|
||||
"Rev": "12d5545dc1cfa6047a286d5e853841b6471f4c19"
|
||||
},
|
||||
{
|
||||
"ImportPath": "google.golang.org/cloud",
|
||||
"Rev": "975617b05ea8a58727e6c1a06b6161ff4185a9f2"
|
||||
},
|
||||
{
|
||||
"ImportPath": "google.golang.org/cloud/compute/metadata",
|
||||
"Rev": "975617b05ea8a58727e6c1a06b6161ff4185a9f2"
|
||||
},
|
||||
{
|
||||
"ImportPath": "google.golang.org/cloud/internal",
|
||||
"Rev": "975617b05ea8a58727e6c1a06b6161ff4185a9f2"
|
||||
},
|
||||
{
|
||||
"ImportPath": "google.golang.org/cloud/internal/opts",
|
||||
"Rev": "975617b05ea8a58727e6c1a06b6161ff4185a9f2"
|
||||
},
|
||||
{
|
||||
"ImportPath": "google.golang.org/cloud/storage",
|
||||
"Rev": "975617b05ea8a58727e6c1a06b6161ff4185a9f2"
|
||||
},
|
||||
{
|
||||
"ImportPath": "google.golang.org/grpc",
|
||||
"Rev": "d3ddb4469d5a1b949fc7a7da7c1d6a0d1b6de994"
|
||||
},
|
||||
{
|
||||
"ImportPath": "google.golang.org/grpc/codes",
|
||||
"Rev": "d3ddb4469d5a1b949fc7a7da7c1d6a0d1b6de994"
|
||||
},
|
||||
{
|
||||
"ImportPath": "google.golang.org/grpc/credentials",
|
||||
"Rev": "d3ddb4469d5a1b949fc7a7da7c1d6a0d1b6de994"
|
||||
},
|
||||
{
|
||||
"ImportPath": "google.golang.org/grpc/grpclog",
|
||||
"Rev": "d3ddb4469d5a1b949fc7a7da7c1d6a0d1b6de994"
|
||||
},
|
||||
{
|
||||
"ImportPath": "google.golang.org/grpc/internal",
|
||||
"Rev": "d3ddb4469d5a1b949fc7a7da7c1d6a0d1b6de994"
|
||||
},
|
||||
{
|
||||
"ImportPath": "google.golang.org/grpc/metadata",
|
||||
"Rev": "d3ddb4469d5a1b949fc7a7da7c1d6a0d1b6de994"
|
||||
},
|
||||
{
|
||||
"ImportPath": "google.golang.org/grpc/naming",
|
||||
"Rev": "d3ddb4469d5a1b949fc7a7da7c1d6a0d1b6de994"
|
||||
},
|
||||
{
|
||||
"ImportPath": "google.golang.org/grpc/peer",
|
||||
"Rev": "d3ddb4469d5a1b949fc7a7da7c1d6a0d1b6de994"
|
||||
},
|
||||
{
|
||||
"ImportPath": "google.golang.org/grpc/transport",
|
||||
"Rev": "d3ddb4469d5a1b949fc7a7da7c1d6a0d1b6de994"
|
||||
},
|
||||
{
|
||||
"ImportPath": "gopkg.in/check.v1",
|
||||
"Rev": "64131543e7896d5bcc6bd5a76287eb75ea96c673"
|
||||
},
|
||||
{
|
||||
"ImportPath": "gopkg.in/yaml.v2",
|
||||
"Rev": "bef53efd0c76e49e6de55ead051f886bea7e9420"
|
||||
},
|
||||
{
|
||||
"ImportPath": "rsc.io/letsencrypt",
|
||||
"Rev": "a019c9e6fce0c7132679dea13bd8df7c86ffe26c"
|
||||
},
|
||||
{
|
||||
"ImportPath": "rsc.io/letsencrypt/vendor/github.com/xenolf/lego/acme",
|
||||
"Rev": "a019c9e6fce0c7132679dea13bd8df7c86ffe26c"
|
||||
},
|
||||
{
|
||||
"ImportPath": "rsc.io/letsencrypt/vendor/gopkg.in/square/go-jose.v1",
|
||||
"Rev": "a019c9e6fce0c7132679dea13bd8df7c86ffe26c"
|
||||
},
|
||||
{
|
||||
"ImportPath": "rsc.io/letsencrypt/vendor/gopkg.in/square/go-jose.v1/cipher",
|
||||
"Rev": "a019c9e6fce0c7132679dea13bd8df7c86ffe26c"
|
||||
},
|
||||
{
|
||||
"ImportPath": "rsc.io/letsencrypt/vendor/gopkg.in/square/go-jose.v1/json",
|
||||
"Rev": "a019c9e6fce0c7132679dea13bd8df7c86ffe26c"
|
||||
}
|
||||
]
|
||||
}
|
||||
5
vendor/github.com/docker/distribution/Godeps/Readme
generated
vendored
5
vendor/github.com/docker/distribution/Godeps/Readme
generated
vendored
@@ -1,5 +0,0 @@
|
||||
This directory tree is generated automatically by godep.
|
||||
|
||||
Please do not edit.
|
||||
|
||||
See https://github.com/tools/godep for more information.
|
||||
26
vendor/github.com/docker/distribution/Makefile
generated
vendored
26
vendor/github.com/docker/distribution/Makefile
generated
vendored
@@ -35,7 +35,7 @@ PKGS=$(shell go list -tags "${DOCKER_BUILDTAGS}" ./... | grep -v ^github.com/doc
|
||||
|
||||
# Resolving binary dependencies for specific targets
|
||||
GOLINT=$(shell which golint || echo '')
|
||||
GODEP=$(shell which godep || echo '')
|
||||
VNDR=$(shell which vndr || echo '')
|
||||
|
||||
${PREFIX}/bin/registry: $(GOFILES)
|
||||
@echo "+ $@"
|
||||
@@ -86,24 +86,14 @@ clean:
|
||||
@echo "+ $@"
|
||||
@rm -rf "${PREFIX}/bin/registry" "${PREFIX}/bin/digest" "${PREFIX}/bin/registry-api-descriptor-template"
|
||||
|
||||
dep-save:
|
||||
@echo "+ $@"
|
||||
$(if $(GODEP), , \
|
||||
$(error Please install godep: go get github.com/tools/godep))
|
||||
@$(GODEP) save $(PKGS)
|
||||
|
||||
dep-restore:
|
||||
@echo "+ $@"
|
||||
$(if $(GODEP), , \
|
||||
$(error Please install godep: go get github.com/tools/godep))
|
||||
@$(GODEP) restore -v
|
||||
|
||||
dep-validate: dep-restore
|
||||
dep-validate:
|
||||
@echo "+ $@"
|
||||
$(if $(VNDR), , \
|
||||
$(error Please install vndr: go get github.com/lk4d4/vndr))
|
||||
@rm -Rf .vendor.bak
|
||||
@mv vendor .vendor.bak
|
||||
@rm -Rf Godeps
|
||||
@$(GODEP) save ./...
|
||||
@$(VNDR)
|
||||
@test -z "$$(diff -r vendor .vendor.bak 2>&1 | tee /dev/stderr)" || \
|
||||
(echo >&2 "+ borked dependencies! what you have in Godeps/Godeps.json does not match with what you have in vendor" && false)
|
||||
@rm -Rf .vendor.bak
|
||||
(echo >&2 "+ inconsistent dependencies! what you have in vendor.conf does not match with what you have in vendor" && false)
|
||||
@rm -Rf vendor
|
||||
@mv .vendor.bak vendor
|
||||
|
||||
3
vendor/github.com/docker/distribution/README.md
generated
vendored
3
vendor/github.com/docker/distribution/README.md
generated
vendored
@@ -76,8 +76,7 @@ may be the better choice.
|
||||
For those who have previously deployed their own registry based on the Registry
|
||||
1.0 implementation and wish to deploy a Registry 2.0 while retaining images,
|
||||
data migration is required. A tool to assist with migration efforts has been
|
||||
created. For more information see [docker/migrator]
|
||||
(https://github.com/docker/migrator).
|
||||
created. For more information see [docker/migrator](https://github.com/docker/migrator).
|
||||
|
||||
## Contribute
|
||||
|
||||
|
||||
16
vendor/github.com/docker/distribution/RELEASE-CHECKLIST.md
generated
vendored
16
vendor/github.com/docker/distribution/RELEASE-CHECKLIST.md
generated
vendored
@@ -1,19 +1,27 @@
|
||||
## Registry Release Checklist
|
||||
|
||||
10. Compile release notes detailing features and since the last release. Update the `CHANGELOG.md` file.
|
||||
10. Compile release notes detailing features and since the last release.
|
||||
|
||||
Update the `CHANGELOG.md` file and create a PR to master with the updates.
|
||||
Once that PR has been approved by maintainers the change may be cherry-picked
|
||||
to the release branch (new release branches may be forked from this commit).
|
||||
|
||||
20. Update the version file: `https://github.com/docker/distribution/blob/master/version/version.go`
|
||||
|
||||
30. Update the `MAINTAINERS` (if necessary), `AUTHORS` and `.mailmap` files.
|
||||
|
||||
```
|
||||
```
|
||||
make AUTHORS
|
||||
```
|
||||
|
||||
40. Create a signed tag.
|
||||
|
||||
Distribution uses semantic versioning. Tags are of the format `vx.y.z[-rcn]`
|
||||
You will need PGP installed and a PGP key which has been added to your Github account. The comment for the tag should include the release notes.
|
||||
Distribution uses semantic versioning. Tags are of the format
|
||||
`vx.y.z[-rcn]`. You will need PGP installed and a PGP key which has been added
|
||||
to your Github account. The comment for the tag should include the release
|
||||
notes, use previous tags as a guide for formatting consistently. Run
|
||||
`git tag -s vx.y.z[-rcn]` to create tag and `git -v vx.y.z[-rcn]` to verify tag,
|
||||
check comment and correct commit hash.
|
||||
|
||||
50. Push the signed tag
|
||||
|
||||
|
||||
4
vendor/github.com/docker/distribution/blobs.go
generated
vendored
4
vendor/github.com/docker/distribution/blobs.go
generated
vendored
@@ -8,8 +8,8 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/docker/distribution/context"
|
||||
"github.com/docker/distribution/digest"
|
||||
"github.com/docker/distribution/reference"
|
||||
"github.com/opencontainers/go-digest"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -152,7 +152,7 @@ type BlobProvider interface {
|
||||
|
||||
// BlobServer can serve blobs via http.
|
||||
type BlobServer interface {
|
||||
// ServeBlob attempts to serve the blob, identifed by dgst, via http. The
|
||||
// ServeBlob attempts to serve the blob, identified by dgst, via http. The
|
||||
// service may decide to redirect the client elsewhere or serve the data
|
||||
// directly.
|
||||
//
|
||||
|
||||
17
vendor/github.com/docker/distribution/circle.yml
generated
vendored
17
vendor/github.com/docker/distribution/circle.yml
generated
vendored
@@ -8,7 +8,7 @@ machine:
|
||||
|
||||
post:
|
||||
# go
|
||||
- gvm install go1.7 --prefer-binary --name=stable
|
||||
- gvm install go1.8 --prefer-binary --name=stable
|
||||
|
||||
environment:
|
||||
# Convenient shortcuts to "common" locations
|
||||
@@ -34,7 +34,7 @@ dependencies:
|
||||
|
||||
override:
|
||||
# Install dependencies for every copied clone/go version
|
||||
- gvm use stable && go get github.com/tools/godep:
|
||||
- gvm use stable && go get github.com/lk4d4/vndr:
|
||||
pwd: $BASE_STABLE
|
||||
|
||||
post:
|
||||
@@ -49,14 +49,15 @@ test:
|
||||
# - gvm use old && go version
|
||||
- gvm use stable && go version
|
||||
|
||||
# todo(richard): replace with a more robust vendoring solution. Removed due to a fundamental disagreement in godep philosophies.
|
||||
# Ensure validation of dependencies
|
||||
# - gvm use stable && if test -n "`git diff --stat=1000 master | grep -Ei \"vendor|godeps\"`"; then make dep-validate; fi:
|
||||
# pwd: $BASE_STABLE
|
||||
- git fetch origin:
|
||||
pwd: $BASE_STABLE
|
||||
- gvm use stable && if test -n "`git diff --stat=1000 origin/master | grep -E \"^[[:space:]]*vendor\"`"; then make dep-validate; fi:
|
||||
pwd: $BASE_STABLE
|
||||
|
||||
# First thing: build everything. This will catch compile errors, and it's
|
||||
# also necessary for go vet to work properly (see #807).
|
||||
- gvm use stable && godep go install $(go list ./... | grep -v "/vendor/"):
|
||||
- gvm use stable && go install $(go list ./... | grep -v "/vendor/"):
|
||||
pwd: $BASE_STABLE
|
||||
|
||||
# FMT
|
||||
@@ -73,12 +74,12 @@ test:
|
||||
|
||||
override:
|
||||
# Test stable, and report
|
||||
- gvm use stable; export ROOT_PACKAGE=$(go list .); go list -tags "$DOCKER_BUILDTAGS" ./... | grep -v "/vendor/" | xargs -L 1 -I{} bash -c 'export PACKAGE={}; godep go test -tags "$DOCKER_BUILDTAGS" -test.short -coverprofile=$GOPATH/src/$PACKAGE/coverage.out -coverpkg=$(./coverpkg.sh $PACKAGE $ROOT_PACKAGE) $PACKAGE':
|
||||
- gvm use stable; export ROOT_PACKAGE=$(go list .); go list -tags "$DOCKER_BUILDTAGS" ./... | grep -v "/vendor/" | xargs -L 1 -I{} bash -c 'export PACKAGE={}; go test -tags "$DOCKER_BUILDTAGS" -test.short -coverprofile=$GOPATH/src/$PACKAGE/coverage.out -coverpkg=$(./coverpkg.sh $PACKAGE $ROOT_PACKAGE) $PACKAGE':
|
||||
timeout: 1000
|
||||
pwd: $BASE_STABLE
|
||||
|
||||
# Test stable with race
|
||||
- gvm use stable; export ROOT_PACKAGE=$(go list .); go list -tags "$DOCKER_BUILDTAGS" ./... | grep -v "/vendor/" | grep -v "registry/handlers" | grep -v "registry/storage/driver" | xargs -L 1 -I{} bash -c 'export PACKAGE={}; godep go test -race -tags "$DOCKER_BUILDTAGS" -test.short $PACKAGE':
|
||||
- gvm use stable; export ROOT_PACKAGE=$(go list .); go list -tags "$DOCKER_BUILDTAGS" ./... | grep -v "/vendor/" | grep -v "registry/handlers" | grep -v "registry/storage/driver" | xargs -L 1 -I{} bash -c 'export PACKAGE={}; go test -race -tags "$DOCKER_BUILDTAGS" -test.short $PACKAGE':
|
||||
timeout: 1000
|
||||
pwd: $BASE_STABLE
|
||||
post:
|
||||
|
||||
4
vendor/github.com/docker/distribution/cmd/digest/main.go
generated
vendored
4
vendor/github.com/docker/distribution/cmd/digest/main.go
generated
vendored
@@ -7,8 +7,8 @@ import (
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/docker/distribution/digest"
|
||||
"github.com/docker/distribution/version"
|
||||
"github.com/opencontainers/go-digest"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -32,7 +32,7 @@ func init() {
|
||||
|
||||
func usage() {
|
||||
fmt.Fprintf(os.Stderr, "usage: %s [files...]\n", os.Args[0])
|
||||
fmt.Fprintf(os.Stderr, `
|
||||
fmt.Fprint(os.Stderr, `
|
||||
Calculate the digest of one or more input files, emitting the result
|
||||
to standard out. If no files are provided, the digest of stdin will
|
||||
be calculated.
|
||||
|
||||
14
vendor/github.com/docker/distribution/configuration/configuration.go
generated
vendored
14
vendor/github.com/docker/distribution/configuration/configuration.go
generated
vendored
@@ -1,6 +1,7 @@
|
||||
package configuration
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
@@ -132,7 +133,7 @@ type Configuration struct {
|
||||
|
||||
// HTTP2 configuration options
|
||||
HTTP2 struct {
|
||||
// Specifies wether the registry should disallow clients attempting
|
||||
// Specifies whether the registry should disallow clients attempting
|
||||
// to connect via http2. If set to true, only http/1.1 is supported.
|
||||
Disabled bool `yaml:"disabled,omitempty"`
|
||||
} `yaml:"http2,omitempty"`
|
||||
@@ -188,8 +189,11 @@ type Configuration struct {
|
||||
|
||||
// Validation configures validation options for the registry.
|
||||
Validation struct {
|
||||
// Enabled enables the other options in this section.
|
||||
// Enabled enables the other options in this section. This field is
|
||||
// deprecated in favor of Disabled.
|
||||
Enabled bool `yaml:"enabled,omitempty"`
|
||||
// Disabled disables the other options in this section.
|
||||
Disabled bool `yaml:"disabled,omitempty"`
|
||||
// Manifests configures manifest validation.
|
||||
Manifests struct {
|
||||
// URLs configures validation for URLs in pushed manifests.
|
||||
@@ -232,7 +236,7 @@ type LogHook struct {
|
||||
// Levels set which levels of log message will let hook executed.
|
||||
Levels []string `yaml:"levels,omitempty"`
|
||||
|
||||
// MailOptions allows user to configurate email parameters.
|
||||
// MailOptions allows user to configure email parameters.
|
||||
MailOptions MailOptions `yaml:"options,omitempty"`
|
||||
}
|
||||
|
||||
@@ -326,7 +330,7 @@ type Health struct {
|
||||
type v0_1Configuration Configuration
|
||||
|
||||
// UnmarshalYAML implements the yaml.Unmarshaler interface
|
||||
// Unmarshals a string of the form X.Y into a Version, validating that X and Y can represent uints
|
||||
// Unmarshals a string of the form X.Y into a Version, validating that X and Y can represent unsigned integers
|
||||
func (version *Version) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
||||
var versionString string
|
||||
err := unmarshal(&versionString)
|
||||
@@ -624,7 +628,7 @@ func Parse(rd io.Reader) (*Configuration, error) {
|
||||
v0_1.Loglevel = Loglevel("info")
|
||||
}
|
||||
if v0_1.Storage.Type() == "" {
|
||||
return nil, fmt.Errorf("No storage configuration provided")
|
||||
return nil, errors.New("No storage configuration provided")
|
||||
}
|
||||
return (*Configuration)(v0_1), nil
|
||||
}
|
||||
|
||||
2
vendor/github.com/docker/distribution/context/doc.go
generated
vendored
2
vendor/github.com/docker/distribution/context/doc.go
generated
vendored
@@ -64,7 +64,7 @@
|
||||
// Note that this only affects the new context, the previous context, with the
|
||||
// version field, can be used independently. Put another way, the new logger,
|
||||
// added to the request context, is unique to that context and can have
|
||||
// request scoped varaibles.
|
||||
// request scoped variables.
|
||||
//
|
||||
// HTTP Requests
|
||||
//
|
||||
|
||||
6
vendor/github.com/docker/distribution/contrib/compose/README.md
generated
vendored
6
vendor/github.com/docker/distribution/contrib/compose/README.md
generated
vendored
@@ -123,13 +123,13 @@ to the 1.0 registry. Requests from newer clients will route to the 2.0 registry.
|
||||
|
||||
4. Use `curl` to list the image in the registry.
|
||||
|
||||
$ curl -v -X GET http://localhost:32777/v2/registry1/tags/list
|
||||
$ curl -v -X GET http://localhost:5000/v2/registry_one/tags/list
|
||||
* Hostname was NOT found in DNS cache
|
||||
* Trying 127.0.0.1...
|
||||
* Connected to localhost (127.0.0.1) port 32777 (#0)
|
||||
> GET /v2/registry1/tags/list HTTP/1.1
|
||||
> User-Agent: curl/7.36.0
|
||||
> Host: localhost:32777
|
||||
> Host: localhost:5000
|
||||
> Accept: */*
|
||||
>
|
||||
< HTTP/1.1 200 OK
|
||||
@@ -138,7 +138,7 @@ to the 1.0 registry. Requests from newer clients will route to the 2.0 registry.
|
||||
< Date: Tue, 14 Apr 2015 22:34:13 GMT
|
||||
< Content-Length: 39
|
||||
<
|
||||
{"name":"registry1","tags":["latest"]}
|
||||
{"name":"registry_one","tags":["latest"]}
|
||||
* Connection #0 to host localhost left intact
|
||||
|
||||
This example refers to the specific port assigned to the 2.0 registry. You saw
|
||||
|
||||
2
vendor/github.com/docker/distribution/contrib/docker-integration/docker-compose.yml
generated
vendored
2
vendor/github.com/docker/distribution/contrib/docker-integration/docker-compose.yml
generated
vendored
@@ -64,7 +64,7 @@ registryv2tokenoauthnotls:
|
||||
- ./tokenserver-oauth/certs/signing.cert:/etc/docker/registry/tokenbundle.pem
|
||||
tokenserveroauth:
|
||||
build: "tokenserver-oauth"
|
||||
command: "--debug -addr 0.0.0.0:5559 -issuer registry-test -passwd .htpasswd -tlscert tls.cert -tlskey tls.key -key sign.key -realm http://auth.localregistry:5559"
|
||||
command: "--debug -addr 0.0.0.0:5559 -issuer registry-test -passwd .htpasswd -tlscert tls.cert -tlskey tls.key -key sign.key -realm http://auth.localregistry:5559 -enforce-class"
|
||||
ports:
|
||||
- "5559"
|
||||
malevolent:
|
||||
|
||||
2
vendor/github.com/docker/distribution/contrib/docker-integration/golem.conf
generated
vendored
2
vendor/github.com/docker/distribution/contrib/docker-integration/golem.conf
generated
vendored
@@ -1,6 +1,6 @@
|
||||
[[suite]]
|
||||
dind=true
|
||||
images=[ "nginx:1.9", "dmcgowan/token-server:simple", "dmcgowan/token-server:oauth", "dmcgowan/malevolent:0.1.0" ]
|
||||
images=[ "nginx:1.9", "dmcgowan/token-server:simple", "dmcgowan/token-server:oauth", "dmcgowan/malevolent:0.1.0", "dmcgowan/ncat:latest" ]
|
||||
|
||||
[[suite.pretest]]
|
||||
command="sh ./install_certs.sh /etc/generated_certs.d"
|
||||
|
||||
34
vendor/github.com/docker/distribution/contrib/docker-integration/helpers.bash
generated
vendored
34
vendor/github.com/docker/distribution/contrib/docker-integration/helpers.bash
generated
vendored
@@ -32,18 +32,44 @@ function basic_auth_version_check() {
|
||||
fi
|
||||
}
|
||||
|
||||
email="a@nowhere.com"
|
||||
|
||||
# docker_t_login calls login with email depending on version
|
||||
function docker_t_login() {
|
||||
# Only pass email field pre 1.11, no deprecation warning
|
||||
parse_version "$GOLEM_DIND_VERSION"
|
||||
v=$version
|
||||
parse_version "1.11.0"
|
||||
if [ "$v" -lt "$version" ]; then
|
||||
run docker_t login -e $email $@
|
||||
else
|
||||
run docker_t login $@
|
||||
fi
|
||||
}
|
||||
|
||||
# login issues a login to docker to the provided server
|
||||
# uses user, password, and email variables set outside of function
|
||||
# requies bats
|
||||
function login() {
|
||||
rm -f /root/.docker/config.json
|
||||
run docker_t login -u $user -p $password -e $email $1
|
||||
|
||||
docker_t_login -u $user -p $password $1
|
||||
if [ "$status" -ne 0 ]; then
|
||||
echo $output
|
||||
fi
|
||||
[ "$status" -eq 0 ]
|
||||
# First line is WARNING about credential save or email deprecation (maybe both)
|
||||
[ "${lines[2]}" = "Login Succeeded" -o "${lines[1]}" = "Login Succeeded" ]
|
||||
|
||||
# Handle different deprecation warnings
|
||||
parse_version "$GOLEM_DIND_VERSION"
|
||||
v=$version
|
||||
parse_version "1.11.0"
|
||||
if [ "$v" -lt "$version" ]; then
|
||||
# First line is WARNING about credential save or email deprecation (maybe both)
|
||||
[ "${lines[2]}" = "Login Succeeded" -o "${lines[1]}" = "Login Succeeded" ]
|
||||
else
|
||||
[ "${lines[0]}" = "Login Succeeded" ]
|
||||
fi
|
||||
|
||||
}
|
||||
|
||||
function login_oauth() {
|
||||
@@ -92,7 +118,7 @@ function docker_t() {
|
||||
docker exec dockerdaemon docker $@
|
||||
}
|
||||
|
||||
# build reates a new docker image id from another image
|
||||
# build creates a new docker image id from another image
|
||||
function build() {
|
||||
docker exec -i dockerdaemon docker build --no-cache -t $1 - <<DOCKERFILE
|
||||
FROM $2
|
||||
|
||||
6
vendor/github.com/docker/distribution/contrib/docker-integration/malevolent.bats
generated
vendored
6
vendor/github.com/docker/distribution/contrib/docker-integration/malevolent.bats
generated
vendored
@@ -12,7 +12,7 @@ function setup() {
|
||||
}
|
||||
|
||||
@test "Test malevolent proxy pass through" {
|
||||
docker_t tag -f $base:latest $host/$base/nochange:latest
|
||||
docker_t tag $base:latest $host/$base/nochange:latest
|
||||
run docker_t push $host/$base/nochange:latest
|
||||
echo $output
|
||||
[ "$status" -eq 0 ]
|
||||
@@ -26,7 +26,7 @@ function setup() {
|
||||
@test "Test malevolent image name change" {
|
||||
imagename="$host/$base/rename"
|
||||
image="$imagename:lastest"
|
||||
docker_t tag -f $base:latest $image
|
||||
docker_t tag $base:latest $image
|
||||
run docker_t push $image
|
||||
[ "$status" -eq 0 ]
|
||||
has_digest "$output"
|
||||
@@ -133,7 +133,7 @@ function setup() {
|
||||
has_digest "$output"
|
||||
|
||||
image2="$host/$base/image2/alteredid:$poison2"
|
||||
docker_t tag -f $image1 $image2
|
||||
docker_t tag $image1 $image2
|
||||
run docker_t push $image2
|
||||
echo "$output"
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
103
vendor/github.com/docker/distribution/contrib/docker-integration/plugins.bats
generated
vendored
Normal file
103
vendor/github.com/docker/distribution/contrib/docker-integration/plugins.bats
generated
vendored
Normal file
@@ -0,0 +1,103 @@
|
||||
#!/usr/bin/env bats
|
||||
|
||||
# This tests pushing and pulling plugins
|
||||
|
||||
load helpers
|
||||
|
||||
user="testuser"
|
||||
password="testpassword"
|
||||
base="hello-world"
|
||||
|
||||
#TODO: Create plugin image
|
||||
function create_plugin() {
|
||||
plugindir=$(mktemp -d)
|
||||
|
||||
cat - > $plugindir/config.json <<CONFIGJSON
|
||||
{
|
||||
"manifestVersion": "v0",
|
||||
"description": "A test plugin for integration tests",
|
||||
"entrypoint": ["/usr/bin/ncat", "-l", "-U", "//run/docker/plugins/plugin.sock"],
|
||||
"interface" : {
|
||||
"types": ["docker.volumedriver/1.0"],
|
||||
"socket": "plugin.sock"
|
||||
}
|
||||
}
|
||||
CONFIGJSON
|
||||
|
||||
cid=$(docker create dmcgowan/ncat:latest /bin/sh)
|
||||
|
||||
mkdir $plugindir/rootfs
|
||||
|
||||
docker export $cid | tar -x -C $plugindir/rootfs
|
||||
|
||||
docker rm $cid
|
||||
|
||||
daemontmp=$(docker exec dockerdaemon mktemp -d)
|
||||
|
||||
tar -c -C $plugindir . | docker exec -i dockerdaemon tar -x -C $daemontmp
|
||||
|
||||
docker exec dockerdaemon docker plugin create $1 $daemontmp
|
||||
|
||||
docker exec dockerdaemon rm -rf $daemontmp
|
||||
|
||||
rm -rf $plugindir
|
||||
}
|
||||
|
||||
@test "Test plugin push and pull" {
|
||||
version_check docker "$GOLEM_DIND_VERSION" "1.13.0-rc3"
|
||||
version_check docker "$GOLEM_DISTRIBUTION_VERSION" "2.6.0"
|
||||
|
||||
login_oauth localregistry:5558
|
||||
image="localregistry:5558/testuser/plugin1"
|
||||
|
||||
create_plugin $image
|
||||
|
||||
run docker_t plugin push $image
|
||||
echo $output
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
docker_t plugin rm $image
|
||||
|
||||
docker_t plugin install --grant-all-permissions $image
|
||||
}
|
||||
|
||||
@test "Test plugin push and failed image pull" {
|
||||
version_check docker "$GOLEM_DIND_VERSION" "1.13.0-rc3"
|
||||
version_check docker "$GOLEM_DISTRIBUTION_VERSION" "2.6.0"
|
||||
|
||||
|
||||
login_oauth localregistry:5558
|
||||
image="localregistry:5558/testuser/plugin-not-image"
|
||||
|
||||
create_plugin $image
|
||||
|
||||
run docker_t plugin push $image
|
||||
echo $output
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
docker_t plugin rm $image
|
||||
|
||||
run docker_t pull $image
|
||||
|
||||
[ "$status" -ne 0 ]
|
||||
}
|
||||
|
||||
@test "Test image push and failed plugin pull" {
|
||||
version_check docker "$GOLEM_DIND_VERSION" "1.13.0-rc3"
|
||||
version_check docker "$GOLEM_DISTRIBUTION_VERSION" "2.6.0"
|
||||
|
||||
login_oauth localregistry:5558
|
||||
image="localregistry:5558/testuser/image-not-plugin"
|
||||
|
||||
build $image "$base:latest"
|
||||
|
||||
run docker_t push $image
|
||||
echo $output
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
docker_t rmi $image
|
||||
|
||||
run docker_t plugin install --grant-all-permissions $image
|
||||
|
||||
[ "$status" -ne 0 ]
|
||||
}
|
||||
5
vendor/github.com/docker/distribution/contrib/docker-integration/run_multiversion.sh
generated
vendored
5
vendor/github.com/docker/distribution/contrib/docker-integration/run_multiversion.sh
generated
vendored
@@ -46,7 +46,6 @@ echo "Testing image $distimage with distribution version $distversion"
|
||||
# These images are defined in golem.conf
|
||||
time docker pull nginx:1.9
|
||||
time docker pull golang:1.6
|
||||
time docker pull registry:0.9.1
|
||||
time docker pull dmcgowan/token-server:simple
|
||||
time docker pull dmcgowan/token-server:oauth
|
||||
time docker pull distribution/golem-runner:0.1-bats
|
||||
@@ -54,11 +53,15 @@ time docker pull distribution/golem-runner:0.1-bats
|
||||
time docker pull docker:1.9.1-dind
|
||||
time docker pull docker:1.10.3-dind
|
||||
time docker pull docker:1.11.1-dind
|
||||
time docker pull docker:1.12.3-dind
|
||||
time docker pull docker:1.13.0-rc5-dind
|
||||
|
||||
golem -cache $cachedir \
|
||||
-i "golem-distribution:latest,$distimage,$distversion" \
|
||||
-i "golem-dind:latest,docker:1.9.1-dind,1.9.1" \
|
||||
-i "golem-dind:latest,docker:1.10.3-dind,1.10.3" \
|
||||
-i "golem-dind:latest,docker:1.11.1-dind,1.11.1" \
|
||||
-i "golem-dind:latest,docker:1.12.3-dind,1.12.3" \
|
||||
-i "golem-dind:latest,docker:1.13.0-rc5-dind,1.13.0" \
|
||||
$DIR
|
||||
|
||||
|
||||
19
vendor/github.com/docker/distribution/contrib/docker-integration/tls.bats
generated
vendored
19
vendor/github.com/docker/distribution/contrib/docker-integration/tls.bats
generated
vendored
@@ -12,14 +12,13 @@ image="${base}:latest"
|
||||
# Login information, should match values in nginx/test.passwd
|
||||
user=${TEST_USER:-"testuser"}
|
||||
password=${TEST_PASSWORD:-"passpassword"}
|
||||
email="distribution@docker.com"
|
||||
|
||||
function setup() {
|
||||
tempImage $image
|
||||
}
|
||||
|
||||
@test "Test valid certificates" {
|
||||
docker_t tag -f $image $hostname:5440/$image
|
||||
docker_t tag $image $hostname:5440/$image
|
||||
run docker_t push $hostname:5440/$image
|
||||
[ "$status" -eq 0 ]
|
||||
has_digest "$output"
|
||||
@@ -28,7 +27,7 @@ function setup() {
|
||||
@test "Test basic auth" {
|
||||
basic_auth_version_check
|
||||
login $hostname:5441
|
||||
docker_t tag -f $image $hostname:5441/$image
|
||||
docker_t tag $image $hostname:5441/$image
|
||||
run docker_t push $hostname:5441/$image
|
||||
[ "$status" -eq 0 ]
|
||||
has_digest "$output"
|
||||
@@ -60,14 +59,14 @@ function setup() {
|
||||
}
|
||||
|
||||
@test "Test TLS client auth" {
|
||||
docker_t tag -f $image $hostname:5442/$image
|
||||
docker_t tag $image $hostname:5442/$image
|
||||
run docker_t push $hostname:5442/$image
|
||||
[ "$status" -eq 0 ]
|
||||
has_digest "$output"
|
||||
}
|
||||
|
||||
@test "Test TLS client with invalid certificate authority fails" {
|
||||
docker_t tag -f $image $hostname:5443/$image
|
||||
docker_t tag $image $hostname:5443/$image
|
||||
run docker_t push $hostname:5443/$image
|
||||
[ "$status" -ne 0 ]
|
||||
}
|
||||
@@ -75,14 +74,14 @@ function setup() {
|
||||
@test "Test basic auth with TLS client auth" {
|
||||
basic_auth_version_check
|
||||
login $hostname:5444
|
||||
docker_t tag -f $image $hostname:5444/$image
|
||||
docker_t tag $image $hostname:5444/$image
|
||||
run docker_t push $hostname:5444/$image
|
||||
[ "$status" -eq 0 ]
|
||||
has_digest "$output"
|
||||
}
|
||||
|
||||
@test "Test unknown certificate authority fails" {
|
||||
docker_t tag -f $image $hostname:5445/$image
|
||||
docker_t tag $image $hostname:5445/$image
|
||||
run docker_t push $hostname:5445/$image
|
||||
[ "$status" -ne 0 ]
|
||||
}
|
||||
@@ -90,19 +89,19 @@ function setup() {
|
||||
@test "Test basic auth with unknown certificate authority fails" {
|
||||
run login $hostname:5446
|
||||
[ "$status" -ne 0 ]
|
||||
docker_t tag -f $image $hostname:5446/$image
|
||||
docker_t tag $image $hostname:5446/$image
|
||||
run docker_t push $hostname:5446/$image
|
||||
[ "$status" -ne 0 ]
|
||||
}
|
||||
|
||||
@test "Test TLS client auth to server with unknown certificate authority fails" {
|
||||
docker_t tag -f $image $hostname:5447/$image
|
||||
docker_t tag $image $hostname:5447/$image
|
||||
run docker_t push $hostname:5447/$image
|
||||
[ "$status" -ne 0 ]
|
||||
}
|
||||
|
||||
@test "Test failure to connect to server fails to fallback to SSLv3" {
|
||||
docker_t tag -f $image $hostname:5448/$image
|
||||
docker_t tag $image $hostname:5448/$image
|
||||
run docker_t push $hostname:5448/$image
|
||||
[ "$status" -ne 0 ]
|
||||
}
|
||||
|
||||
16
vendor/github.com/docker/distribution/contrib/docker-integration/token.bats
generated
vendored
16
vendor/github.com/docker/distribution/contrib/docker-integration/token.bats
generated
vendored
@@ -6,23 +6,17 @@ load helpers
|
||||
|
||||
user="testuser"
|
||||
password="testpassword"
|
||||
email="a@nowhere.com"
|
||||
base="hello-world"
|
||||
|
||||
@test "Test token server login" {
|
||||
run docker_t login -u $user -p $password -e $email localregistry:5554
|
||||
echo $output
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
# First line is WARNING about credential save or email deprecation
|
||||
[ "${lines[2]}" = "Login Succeeded" -o "${lines[1]}" = "Login Succeeded" ]
|
||||
login localregistry:5554
|
||||
}
|
||||
|
||||
@test "Test token server bad login" {
|
||||
run docker_t login -u "testuser" -p "badpassword" -e $email localregistry:5554
|
||||
docker_t_login -u "testuser" -p "badpassword" localregistry:5554
|
||||
[ "$status" -ne 0 ]
|
||||
|
||||
run docker_t login -u "baduser" -p "testpassword" -e $email localregistry:5554
|
||||
docker_t_login -u "baduser" -p "testpassword" localregistry:5554
|
||||
[ "$status" -ne 0 ]
|
||||
}
|
||||
|
||||
@@ -58,10 +52,10 @@ base="hello-world"
|
||||
@test "Test oauth token server bad login" {
|
||||
version_check docker "$GOLEM_DIND_VERSION" "1.11.0"
|
||||
|
||||
run docker_t login -u "testuser" -p "badpassword" -e $email localregistry:5557
|
||||
docker_t_login -u "testuser" -p "badpassword" -e $email localregistry:5557
|
||||
[ "$status" -ne 0 ]
|
||||
|
||||
run docker_t login -u "baduser" -p "testpassword" -e $email localregistry:5557
|
||||
docker_t_login -u "baduser" -p "testpassword" -e $email localregistry:5557
|
||||
[ "$status" -ne 0 ]
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM dmcgowan/token-server:oauth
|
||||
FROM dmcgowan/token-server@sha256:5a6f76d3086cdf63249c77b521108387b49d85a30c5e1c4fe82fdf5ae3b76ba7
|
||||
|
||||
WORKDIR /
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM dmcgowan/token-server:simple
|
||||
FROM dmcgowan/token-server@sha256:0eab50ebdff5b6b95b3addf4edbd8bd2f5b940f27b41b43c94afdf05863a81af
|
||||
|
||||
WORKDIR /
|
||||
|
||||
|
||||
139
vendor/github.com/docker/distribution/digest/digest.go
generated
vendored
139
vendor/github.com/docker/distribution/digest/digest.go
generated
vendored
@@ -1,139 +0,0 @@
|
||||
package digest
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"hash"
|
||||
"io"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
// DigestSha256EmptyTar is the canonical sha256 digest of empty data
|
||||
DigestSha256EmptyTar = "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
|
||||
)
|
||||
|
||||
// Digest allows simple protection of hex formatted digest strings, prefixed
|
||||
// by their algorithm. Strings of type Digest have some guarantee of being in
|
||||
// the correct format and it provides quick access to the components of a
|
||||
// digest string.
|
||||
//
|
||||
// The following is an example of the contents of Digest types:
|
||||
//
|
||||
// sha256:7173b809ca12ec5dee4506cd86be934c4596dd234ee82c0662eac04a8c2c71dc
|
||||
//
|
||||
// This allows to abstract the digest behind this type and work only in those
|
||||
// terms.
|
||||
type Digest string
|
||||
|
||||
// NewDigest returns a Digest from alg and a hash.Hash object.
|
||||
func NewDigest(alg Algorithm, h hash.Hash) Digest {
|
||||
return NewDigestFromBytes(alg, h.Sum(nil))
|
||||
}
|
||||
|
||||
// NewDigestFromBytes returns a new digest from the byte contents of p.
|
||||
// Typically, this can come from hash.Hash.Sum(...) or xxx.SumXXX(...)
|
||||
// functions. This is also useful for rebuilding digests from binary
|
||||
// serializations.
|
||||
func NewDigestFromBytes(alg Algorithm, p []byte) Digest {
|
||||
return Digest(fmt.Sprintf("%s:%x", alg, p))
|
||||
}
|
||||
|
||||
// NewDigestFromHex returns a Digest from alg and a the hex encoded digest.
|
||||
func NewDigestFromHex(alg, hex string) Digest {
|
||||
return Digest(fmt.Sprintf("%s:%s", alg, hex))
|
||||
}
|
||||
|
||||
// DigestRegexp matches valid digest types.
|
||||
var DigestRegexp = regexp.MustCompile(`[a-zA-Z0-9-_+.]+:[a-fA-F0-9]+`)
|
||||
|
||||
// DigestRegexpAnchored matches valid digest types, anchored to the start and end of the match.
|
||||
var DigestRegexpAnchored = regexp.MustCompile(`^` + DigestRegexp.String() + `$`)
|
||||
|
||||
var (
|
||||
// ErrDigestInvalidFormat returned when digest format invalid.
|
||||
ErrDigestInvalidFormat = fmt.Errorf("invalid checksum digest format")
|
||||
|
||||
// ErrDigestInvalidLength returned when digest has invalid length.
|
||||
ErrDigestInvalidLength = fmt.Errorf("invalid checksum digest length")
|
||||
|
||||
// ErrDigestUnsupported returned when the digest algorithm is unsupported.
|
||||
ErrDigestUnsupported = fmt.Errorf("unsupported digest algorithm")
|
||||
)
|
||||
|
||||
// ParseDigest parses s and returns the validated digest object. An error will
|
||||
// be returned if the format is invalid.
|
||||
func ParseDigest(s string) (Digest, error) {
|
||||
d := Digest(s)
|
||||
|
||||
return d, d.Validate()
|
||||
}
|
||||
|
||||
// FromReader returns the most valid digest for the underlying content using
|
||||
// the canonical digest algorithm.
|
||||
func FromReader(rd io.Reader) (Digest, error) {
|
||||
return Canonical.FromReader(rd)
|
||||
}
|
||||
|
||||
// FromBytes digests the input and returns a Digest.
|
||||
func FromBytes(p []byte) Digest {
|
||||
return Canonical.FromBytes(p)
|
||||
}
|
||||
|
||||
// Validate checks that the contents of d is a valid digest, returning an
|
||||
// error if not.
|
||||
func (d Digest) Validate() error {
|
||||
s := string(d)
|
||||
|
||||
if !DigestRegexpAnchored.MatchString(s) {
|
||||
return ErrDigestInvalidFormat
|
||||
}
|
||||
|
||||
i := strings.Index(s, ":")
|
||||
if i < 0 {
|
||||
return ErrDigestInvalidFormat
|
||||
}
|
||||
|
||||
// case: "sha256:" with no hex.
|
||||
if i+1 == len(s) {
|
||||
return ErrDigestInvalidFormat
|
||||
}
|
||||
|
||||
switch algorithm := Algorithm(s[:i]); algorithm {
|
||||
case SHA256, SHA384, SHA512:
|
||||
if algorithm.Size()*2 != len(s[i+1:]) {
|
||||
return ErrDigestInvalidLength
|
||||
}
|
||||
break
|
||||
default:
|
||||
return ErrDigestUnsupported
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Algorithm returns the algorithm portion of the digest. This will panic if
|
||||
// the underlying digest is not in a valid format.
|
||||
func (d Digest) Algorithm() Algorithm {
|
||||
return Algorithm(d[:d.sepIndex()])
|
||||
}
|
||||
|
||||
// Hex returns the hex digest portion of the digest. This will panic if the
|
||||
// underlying digest is not in a valid format.
|
||||
func (d Digest) Hex() string {
|
||||
return string(d[d.sepIndex()+1:])
|
||||
}
|
||||
|
||||
func (d Digest) String() string {
|
||||
return string(d)
|
||||
}
|
||||
|
||||
func (d Digest) sepIndex() int {
|
||||
i := strings.Index(string(d), ":")
|
||||
|
||||
if i < 0 {
|
||||
panic("could not find ':' in digest: " + d)
|
||||
}
|
||||
|
||||
return i
|
||||
}
|
||||
82
vendor/github.com/docker/distribution/digest/digest_test.go
generated
vendored
82
vendor/github.com/docker/distribution/digest/digest_test.go
generated
vendored
@@ -1,82 +0,0 @@
|
||||
package digest
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestParseDigest(t *testing.T) {
|
||||
for _, testcase := range []struct {
|
||||
input string
|
||||
err error
|
||||
algorithm Algorithm
|
||||
hex string
|
||||
}{
|
||||
{
|
||||
input: "sha256:e58fcf7418d4390dec8e8fb69d88c06ec07039d651fedd3aa72af9972e7d046b",
|
||||
algorithm: "sha256",
|
||||
hex: "e58fcf7418d4390dec8e8fb69d88c06ec07039d651fedd3aa72af9972e7d046b",
|
||||
},
|
||||
{
|
||||
input: "sha384:d3fc7881460b7e22e3d172954463dddd7866d17597e7248453c48b3e9d26d9596bf9c4a9cf8072c9d5bad76e19af801d",
|
||||
algorithm: "sha384",
|
||||
hex: "d3fc7881460b7e22e3d172954463dddd7866d17597e7248453c48b3e9d26d9596bf9c4a9cf8072c9d5bad76e19af801d",
|
||||
},
|
||||
{
|
||||
// empty hex
|
||||
input: "sha256:",
|
||||
err: ErrDigestInvalidFormat,
|
||||
},
|
||||
{
|
||||
// just hex
|
||||
input: "d41d8cd98f00b204e9800998ecf8427e",
|
||||
err: ErrDigestInvalidFormat,
|
||||
},
|
||||
{
|
||||
// not hex
|
||||
input: "sha256:d41d8cd98f00b204e9800m98ecf8427e",
|
||||
err: ErrDigestInvalidFormat,
|
||||
},
|
||||
{
|
||||
// too short
|
||||
input: "sha256:abcdef0123456789",
|
||||
err: ErrDigestInvalidLength,
|
||||
},
|
||||
{
|
||||
// too short (from different algorithm)
|
||||
input: "sha512:abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789",
|
||||
err: ErrDigestInvalidLength,
|
||||
},
|
||||
{
|
||||
input: "foo:d41d8cd98f00b204e9800998ecf8427e",
|
||||
err: ErrDigestUnsupported,
|
||||
},
|
||||
} {
|
||||
digest, err := ParseDigest(testcase.input)
|
||||
if err != testcase.err {
|
||||
t.Fatalf("error differed from expected while parsing %q: %v != %v", testcase.input, err, testcase.err)
|
||||
}
|
||||
|
||||
if testcase.err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if digest.Algorithm() != testcase.algorithm {
|
||||
t.Fatalf("incorrect algorithm for parsed digest: %q != %q", digest.Algorithm(), testcase.algorithm)
|
||||
}
|
||||
|
||||
if digest.Hex() != testcase.hex {
|
||||
t.Fatalf("incorrect hex for parsed digest: %q != %q", digest.Hex(), testcase.hex)
|
||||
}
|
||||
|
||||
// Parse string return value and check equality
|
||||
newParsed, err := ParseDigest(digest.String())
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error parsing input %q: %v", testcase.input, err)
|
||||
}
|
||||
|
||||
if newParsed != digest {
|
||||
t.Fatalf("expected equal: %q != %q", newParsed, digest)
|
||||
}
|
||||
}
|
||||
}
|
||||
155
vendor/github.com/docker/distribution/digest/digester.go
generated
vendored
155
vendor/github.com/docker/distribution/digest/digester.go
generated
vendored
@@ -1,155 +0,0 @@
|
||||
package digest
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"fmt"
|
||||
"hash"
|
||||
"io"
|
||||
)
|
||||
|
||||
// Algorithm identifies and implementation of a digester by an identifier.
|
||||
// Note the that this defines both the hash algorithm used and the string
|
||||
// encoding.
|
||||
type Algorithm string
|
||||
|
||||
// supported digest types
|
||||
const (
|
||||
SHA256 Algorithm = "sha256" // sha256 with hex encoding
|
||||
SHA384 Algorithm = "sha384" // sha384 with hex encoding
|
||||
SHA512 Algorithm = "sha512" // sha512 with hex encoding
|
||||
|
||||
// Canonical is the primary digest algorithm used with the distribution
|
||||
// project. Other digests may be used but this one is the primary storage
|
||||
// digest.
|
||||
Canonical = SHA256
|
||||
)
|
||||
|
||||
var (
|
||||
// TODO(stevvooe): Follow the pattern of the standard crypto package for
|
||||
// registration of digests. Effectively, we are a registerable set and
|
||||
// common symbol access.
|
||||
|
||||
// algorithms maps values to hash.Hash implementations. Other algorithms
|
||||
// may be available but they cannot be calculated by the digest package.
|
||||
algorithms = map[Algorithm]crypto.Hash{
|
||||
SHA256: crypto.SHA256,
|
||||
SHA384: crypto.SHA384,
|
||||
SHA512: crypto.SHA512,
|
||||
}
|
||||
)
|
||||
|
||||
// Available returns true if the digest type is available for use. If this
|
||||
// returns false, New and Hash will return nil.
|
||||
func (a Algorithm) Available() bool {
|
||||
h, ok := algorithms[a]
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
// check availability of the hash, as well
|
||||
return h.Available()
|
||||
}
|
||||
|
||||
func (a Algorithm) String() string {
|
||||
return string(a)
|
||||
}
|
||||
|
||||
// Size returns number of bytes returned by the hash.
|
||||
func (a Algorithm) Size() int {
|
||||
h, ok := algorithms[a]
|
||||
if !ok {
|
||||
return 0
|
||||
}
|
||||
return h.Size()
|
||||
}
|
||||
|
||||
// Set implemented to allow use of Algorithm as a command line flag.
|
||||
func (a *Algorithm) Set(value string) error {
|
||||
if value == "" {
|
||||
*a = Canonical
|
||||
} else {
|
||||
// just do a type conversion, support is queried with Available.
|
||||
*a = Algorithm(value)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// New returns a new digester for the specified algorithm. If the algorithm
|
||||
// does not have a digester implementation, nil will be returned. This can be
|
||||
// checked by calling Available before calling New.
|
||||
func (a Algorithm) New() Digester {
|
||||
return &digester{
|
||||
alg: a,
|
||||
hash: a.Hash(),
|
||||
}
|
||||
}
|
||||
|
||||
// Hash returns a new hash as used by the algorithm. If not available, the
|
||||
// method will panic. Check Algorithm.Available() before calling.
|
||||
func (a Algorithm) Hash() hash.Hash {
|
||||
if !a.Available() {
|
||||
// NOTE(stevvooe): A missing hash is usually a programming error that
|
||||
// must be resolved at compile time. We don't import in the digest
|
||||
// package to allow users to choose their hash implementation (such as
|
||||
// when using stevvooe/resumable or a hardware accelerated package).
|
||||
//
|
||||
// Applications that may want to resolve the hash at runtime should
|
||||
// call Algorithm.Available before call Algorithm.Hash().
|
||||
panic(fmt.Sprintf("%v not available (make sure it is imported)", a))
|
||||
}
|
||||
|
||||
return algorithms[a].New()
|
||||
}
|
||||
|
||||
// FromReader returns the digest of the reader using the algorithm.
|
||||
func (a Algorithm) FromReader(rd io.Reader) (Digest, error) {
|
||||
digester := a.New()
|
||||
|
||||
if _, err := io.Copy(digester.Hash(), rd); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return digester.Digest(), nil
|
||||
}
|
||||
|
||||
// FromBytes digests the input and returns a Digest.
|
||||
func (a Algorithm) FromBytes(p []byte) Digest {
|
||||
digester := a.New()
|
||||
|
||||
if _, err := digester.Hash().Write(p); err != nil {
|
||||
// Writes to a Hash should never fail. None of the existing
|
||||
// hash implementations in the stdlib or hashes vendored
|
||||
// here can return errors from Write. Having a panic in this
|
||||
// condition instead of having FromBytes return an error value
|
||||
// avoids unnecessary error handling paths in all callers.
|
||||
panic("write to hash function returned error: " + err.Error())
|
||||
}
|
||||
|
||||
return digester.Digest()
|
||||
}
|
||||
|
||||
// TODO(stevvooe): Allow resolution of verifiers using the digest type and
|
||||
// this registration system.
|
||||
|
||||
// Digester calculates the digest of written data. Writes should go directly
|
||||
// to the return value of Hash, while calling Digest will return the current
|
||||
// value of the digest.
|
||||
type Digester interface {
|
||||
Hash() hash.Hash // provides direct access to underlying hash instance.
|
||||
Digest() Digest
|
||||
}
|
||||
|
||||
// digester provides a simple digester definition that embeds a hasher.
|
||||
type digester struct {
|
||||
alg Algorithm
|
||||
hash hash.Hash
|
||||
}
|
||||
|
||||
func (d *digester) Hash() hash.Hash {
|
||||
return d.hash
|
||||
}
|
||||
|
||||
func (d *digester) Digest() Digest {
|
||||
return NewDigest(d.alg, d.hash)
|
||||
}
|
||||
42
vendor/github.com/docker/distribution/digest/doc.go
generated
vendored
42
vendor/github.com/docker/distribution/digest/doc.go
generated
vendored
@@ -1,42 +0,0 @@
|
||||
// Package digest provides a generalized type to opaquely represent message
|
||||
// digests and their operations within the registry. The Digest type is
|
||||
// designed to serve as a flexible identifier in a content-addressable system.
|
||||
// More importantly, it provides tools and wrappers to work with
|
||||
// hash.Hash-based digests with little effort.
|
||||
//
|
||||
// Basics
|
||||
//
|
||||
// The format of a digest is simply a string with two parts, dubbed the
|
||||
// "algorithm" and the "digest", separated by a colon:
|
||||
//
|
||||
// <algorithm>:<digest>
|
||||
//
|
||||
// An example of a sha256 digest representation follows:
|
||||
//
|
||||
// sha256:7173b809ca12ec5dee4506cd86be934c4596dd234ee82c0662eac04a8c2c71dc
|
||||
//
|
||||
// In this case, the string "sha256" is the algorithm and the hex bytes are
|
||||
// the "digest".
|
||||
//
|
||||
// Because the Digest type is simply a string, once a valid Digest is
|
||||
// obtained, comparisons are cheap, quick and simple to express with the
|
||||
// standard equality operator.
|
||||
//
|
||||
// Verification
|
||||
//
|
||||
// The main benefit of using the Digest type is simple verification against a
|
||||
// given digest. The Verifier interface, modeled after the stdlib hash.Hash
|
||||
// interface, provides a common write sink for digest verification. After
|
||||
// writing is complete, calling the Verifier.Verified method will indicate
|
||||
// whether or not the stream of bytes matches the target digest.
|
||||
//
|
||||
// Missing Features
|
||||
//
|
||||
// In addition to the above, we intend to add the following features to this
|
||||
// package:
|
||||
//
|
||||
// 1. A Digester type that supports write sink digest calculation.
|
||||
//
|
||||
// 2. Suspend and resume of ongoing digest calculations to support efficient digest verification in the registry.
|
||||
//
|
||||
package digest
|
||||
44
vendor/github.com/docker/distribution/digest/verifiers.go
generated
vendored
44
vendor/github.com/docker/distribution/digest/verifiers.go
generated
vendored
@@ -1,44 +0,0 @@
|
||||
package digest
|
||||
|
||||
import (
|
||||
"hash"
|
||||
"io"
|
||||
)
|
||||
|
||||
// Verifier presents a general verification interface to be used with message
|
||||
// digests and other byte stream verifications. Users instantiate a Verifier
|
||||
// from one of the various methods, write the data under test to it then check
|
||||
// the result with the Verified method.
|
||||
type Verifier interface {
|
||||
io.Writer
|
||||
|
||||
// Verified will return true if the content written to Verifier matches
|
||||
// the digest.
|
||||
Verified() bool
|
||||
}
|
||||
|
||||
// NewDigestVerifier returns a verifier that compares the written bytes
|
||||
// against a passed in digest.
|
||||
func NewDigestVerifier(d Digest) (Verifier, error) {
|
||||
if err := d.Validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return hashVerifier{
|
||||
hash: d.Algorithm().Hash(),
|
||||
digest: d,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type hashVerifier struct {
|
||||
digest Digest
|
||||
hash hash.Hash
|
||||
}
|
||||
|
||||
func (hv hashVerifier) Write(p []byte) (n int, err error) {
|
||||
return hv.hash.Write(p)
|
||||
}
|
||||
|
||||
func (hv hashVerifier) Verified() bool {
|
||||
return hv.digest == NewDigest(hv.digest.Algorithm(), hv.hash)
|
||||
}
|
||||
49
vendor/github.com/docker/distribution/digest/verifiers_test.go
generated
vendored
49
vendor/github.com/docker/distribution/digest/verifiers_test.go
generated
vendored
@@ -1,49 +0,0 @@
|
||||
package digest
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/rand"
|
||||
"io"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestDigestVerifier(t *testing.T) {
|
||||
p := make([]byte, 1<<20)
|
||||
rand.Read(p)
|
||||
digest := FromBytes(p)
|
||||
|
||||
verifier, err := NewDigestVerifier(digest)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error getting digest verifier: %s", err)
|
||||
}
|
||||
|
||||
io.Copy(verifier, bytes.NewReader(p))
|
||||
|
||||
if !verifier.Verified() {
|
||||
t.Fatalf("bytes not verified")
|
||||
}
|
||||
}
|
||||
|
||||
// TestVerifierUnsupportedDigest ensures that unsupported digest validation is
|
||||
// flowing through verifier creation.
|
||||
func TestVerifierUnsupportedDigest(t *testing.T) {
|
||||
unsupported := Digest("bean:0123456789abcdef")
|
||||
|
||||
_, err := NewDigestVerifier(unsupported)
|
||||
if err == nil {
|
||||
t.Fatalf("expected error when creating verifier")
|
||||
}
|
||||
|
||||
if err != ErrDigestUnsupported {
|
||||
t.Fatalf("incorrect error for unsupported digest: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(stevvooe): Add benchmarks to measure bytes/second throughput for
|
||||
// DigestVerifier.
|
||||
//
|
||||
// The relevant benchmark for comparison can be run with the following
|
||||
// commands:
|
||||
//
|
||||
// go test -bench . crypto/sha1
|
||||
//
|
||||
@@ -1,10 +1,12 @@
|
||||
package digest
|
||||
package digestset
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
digest "github.com/opencontainers/go-digest"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -44,7 +46,7 @@ func NewSet() *Set {
|
||||
// values or short values. This function does not test equality,
|
||||
// rather whether the second value could match against the first
|
||||
// value.
|
||||
func checkShortMatch(alg Algorithm, hex, shortAlg, shortHex string) bool {
|
||||
func checkShortMatch(alg digest.Algorithm, hex, shortAlg, shortHex string) bool {
|
||||
if len(hex) == len(shortHex) {
|
||||
if hex != shortHex {
|
||||
return false
|
||||
@@ -64,7 +66,7 @@ func checkShortMatch(alg Algorithm, hex, shortAlg, shortHex string) bool {
|
||||
// If no digests could be found ErrDigestNotFound will be returned
|
||||
// with an empty digest value. If multiple matches are found
|
||||
// ErrDigestAmbiguous will be returned with an empty digest value.
|
||||
func (dst *Set) Lookup(d string) (Digest, error) {
|
||||
func (dst *Set) Lookup(d string) (digest.Digest, error) {
|
||||
dst.mutex.RLock()
|
||||
defer dst.mutex.RUnlock()
|
||||
if len(dst.entries) == 0 {
|
||||
@@ -72,11 +74,11 @@ func (dst *Set) Lookup(d string) (Digest, error) {
|
||||
}
|
||||
var (
|
||||
searchFunc func(int) bool
|
||||
alg Algorithm
|
||||
alg digest.Algorithm
|
||||
hex string
|
||||
)
|
||||
dgst, err := ParseDigest(d)
|
||||
if err == ErrDigestInvalidFormat {
|
||||
dgst, err := digest.Parse(d)
|
||||
if err == digest.ErrDigestInvalidFormat {
|
||||
hex = d
|
||||
searchFunc = func(i int) bool {
|
||||
return dst.entries[i].val >= d
|
||||
@@ -108,7 +110,7 @@ func (dst *Set) Lookup(d string) (Digest, error) {
|
||||
// Add adds the given digest to the set. An error will be returned
|
||||
// if the given digest is invalid. If the digest already exists in the
|
||||
// set, this operation will be a no-op.
|
||||
func (dst *Set) Add(d Digest) error {
|
||||
func (dst *Set) Add(d digest.Digest) error {
|
||||
if err := d.Validate(); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -139,7 +141,7 @@ func (dst *Set) Add(d Digest) error {
|
||||
// Remove removes the given digest from the set. An err will be
|
||||
// returned if the given digest is invalid. If the digest does
|
||||
// not exist in the set, this operation will be a no-op.
|
||||
func (dst *Set) Remove(d Digest) error {
|
||||
func (dst *Set) Remove(d digest.Digest) error {
|
||||
if err := d.Validate(); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -167,10 +169,10 @@ func (dst *Set) Remove(d Digest) error {
|
||||
}
|
||||
|
||||
// All returns all the digests in the set
|
||||
func (dst *Set) All() []Digest {
|
||||
func (dst *Set) All() []digest.Digest {
|
||||
dst.mutex.RLock()
|
||||
defer dst.mutex.RUnlock()
|
||||
retValues := make([]Digest, len(dst.entries))
|
||||
retValues := make([]digest.Digest, len(dst.entries))
|
||||
for i := range dst.entries {
|
||||
retValues[i] = dst.entries[i].digest
|
||||
}
|
||||
@@ -183,10 +185,10 @@ func (dst *Set) All() []Digest {
|
||||
// entire value of digest if uniqueness cannot be achieved without the
|
||||
// full value. This function will attempt to make short codes as short
|
||||
// as possible to be unique.
|
||||
func ShortCodeTable(dst *Set, length int) map[Digest]string {
|
||||
func ShortCodeTable(dst *Set, length int) map[digest.Digest]string {
|
||||
dst.mutex.RLock()
|
||||
defer dst.mutex.RUnlock()
|
||||
m := make(map[Digest]string, len(dst.entries))
|
||||
m := make(map[digest.Digest]string, len(dst.entries))
|
||||
l := length
|
||||
resetIdx := 0
|
||||
for i := 0; i < len(dst.entries); i++ {
|
||||
@@ -222,9 +224,9 @@ func ShortCodeTable(dst *Set, length int) map[Digest]string {
|
||||
}
|
||||
|
||||
type digestEntry struct {
|
||||
alg Algorithm
|
||||
alg digest.Algorithm
|
||||
val string
|
||||
digest Digest
|
||||
digest digest.Digest
|
||||
}
|
||||
|
||||
type digestEntries []*digestEntry
|
||||
@@ -1,20 +1,23 @@
|
||||
package digest
|
||||
package digestset
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
_ "crypto/sha512"
|
||||
"encoding/binary"
|
||||
"math/rand"
|
||||
"testing"
|
||||
|
||||
digest "github.com/opencontainers/go-digest"
|
||||
)
|
||||
|
||||
func assertEqualDigests(t *testing.T, d1, d2 Digest) {
|
||||
func assertEqualDigests(t *testing.T, d1, d2 digest.Digest) {
|
||||
if d1 != d2 {
|
||||
t.Fatalf("Digests do not match:\n\tActual: %s\n\tExpected: %s", d1, d2)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLookup(t *testing.T) {
|
||||
digests := []Digest{
|
||||
digests := []digest.Digest{
|
||||
"sha256:1234511111111111111111111111111111111111111111111111111111111111",
|
||||
"sha256:1234111111111111111111111111111111111111111111111111111111111111",
|
||||
"sha256:1234611111111111111111111111111111111111111111111111111111111111",
|
||||
@@ -88,7 +91,7 @@ func TestLookup(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestAddDuplication(t *testing.T) {
|
||||
digests := []Digest{
|
||||
digests := []digest.Digest{
|
||||
"sha256:1234111111111111111111111111111111111111111111111111111111111111",
|
||||
"sha256:1234511111111111111111111111111111111111111111111111111111111111",
|
||||
"sha256:1234611111111111111111111111111111111111111111111111111111111111",
|
||||
@@ -110,7 +113,7 @@ func TestAddDuplication(t *testing.T) {
|
||||
t.Fatal("Invalid dset size")
|
||||
}
|
||||
|
||||
if err := dset.Add(Digest("sha256:1234511111111111111111111111111111111111111111111111111111111111")); err != nil {
|
||||
if err := dset.Add(digest.Digest("sha256:1234511111111111111111111111111111111111111111111111111111111111")); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -118,7 +121,7 @@ func TestAddDuplication(t *testing.T) {
|
||||
t.Fatal("Duplicate digest insert allowed")
|
||||
}
|
||||
|
||||
if err := dset.Add(Digest("sha384:123451111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111")); err != nil {
|
||||
if err := dset.Add(digest.Digest("sha384:123451111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111")); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -170,7 +173,7 @@ func TestAll(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
all := map[Digest]struct{}{}
|
||||
all := map[digest.Digest]struct{}{}
|
||||
for _, dgst := range dset.All() {
|
||||
all[dgst] = struct{}{}
|
||||
}
|
||||
@@ -194,7 +197,7 @@ func assertEqualShort(t *testing.T, actual, expected string) {
|
||||
}
|
||||
|
||||
func TestShortCodeTable(t *testing.T) {
|
||||
digests := []Digest{
|
||||
digests := []digest.Digest{
|
||||
"sha256:1234111111111111111111111111111111111111111111111111111111111111",
|
||||
"sha256:1234511111111111111111111111111111111111111111111111111111111111",
|
||||
"sha256:1234611111111111111111111111111111111111111111111111111111111111",
|
||||
@@ -227,15 +230,15 @@ func TestShortCodeTable(t *testing.T) {
|
||||
assertEqualShort(t, dump[digests[7]], "653")
|
||||
}
|
||||
|
||||
func createDigests(count int) ([]Digest, error) {
|
||||
func createDigests(count int) ([]digest.Digest, error) {
|
||||
r := rand.New(rand.NewSource(25823))
|
||||
digests := make([]Digest, count)
|
||||
digests := make([]digest.Digest, count)
|
||||
for i := range digests {
|
||||
h := sha256.New()
|
||||
if err := binary.Write(h, binary.BigEndian, r.Int63()); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
digests[i] = NewDigest("sha256", h)
|
||||
digests[i] = digest.NewDigest("sha256", h)
|
||||
}
|
||||
return digests, nil
|
||||
}
|
||||
52
vendor/github.com/docker/distribution/docs/architecture.md
generated
vendored
Normal file
52
vendor/github.com/docker/distribution/docs/architecture.md
generated
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
---
|
||||
published: false
|
||||
---
|
||||
|
||||
# Architecture
|
||||
|
||||
## Design
|
||||
**TODO(stevvooe):** Discuss the architecture of the registry, internally and externally, in a few different deployment scenarios.
|
||||
|
||||
### Eventual Consistency
|
||||
|
||||
> **NOTE:** This section belongs somewhere, perhaps in a design document. We
|
||||
> are leaving this here so the information is not lost.
|
||||
|
||||
Running the registry on eventually consistent backends has been part of the
|
||||
design from the beginning. This section covers some of the approaches to
|
||||
dealing with this reality.
|
||||
|
||||
There are a few classes of issues that we need to worry about when
|
||||
implementing something on top of the storage drivers:
|
||||
|
||||
1. Read-After-Write consistency (see this [article on
|
||||
s3](http://shlomoswidler.com/2009/12/read-after-write-consistency-in-amazon.html)).
|
||||
2. [Write-Write Conflicts](http://en.wikipedia.org/wiki/Write%E2%80%93write_conflict).
|
||||
|
||||
In reality, the registry must worry about these kinds of errors when doing the
|
||||
following:
|
||||
|
||||
1. Accepting data into a temporary upload file may not have latest data block
|
||||
yet (read-after-write).
|
||||
2. Moving uploaded data into its blob location (write-write race).
|
||||
3. Modifying the "current" manifest for given tag (write-write race).
|
||||
4. A whole slew of operations around deletes (read-after-write, delete-write
|
||||
races, garbage collection, etc.).
|
||||
|
||||
The backend path layout employs a few techniques to avoid these problems:
|
||||
|
||||
1. Large writes are done to private upload directories. This alleviates most
|
||||
of the corruption potential under multiple writers by avoiding multiple
|
||||
writers.
|
||||
2. Constraints in storage driver implementations, such as support for writing
|
||||
after the end of a file to extend it.
|
||||
3. Digest verification to avoid data corruption.
|
||||
4. Manifest files are stored by digest and cannot change.
|
||||
5. All other non-content files (links, hashes, etc.) are written as an atomic
|
||||
unit. Anything that requires additions and deletions is broken out into
|
||||
separate "files". Last writer still wins.
|
||||
|
||||
Unfortunately, one must play this game when trying to build something like
|
||||
this on top of eventually consistent storage systems. If we run into serious
|
||||
problems, we can wrap the storagedrivers in a shared consistency layer but
|
||||
that would increase complexity and hinder registry cluster performance.
|
||||
29
vendor/github.com/docker/distribution/docs/configuration.md
generated
vendored
29
vendor/github.com/docker/distribution/docs/configuration.md
generated
vendored
@@ -223,9 +223,9 @@ notifications:
|
||||
disabled: false
|
||||
url: https://my.listener.com/event
|
||||
headers: <http.Header>
|
||||
timeout: 500
|
||||
threshold: 5
|
||||
backoff: 1000
|
||||
timeout: 1s
|
||||
threshold: 10
|
||||
backoff: 1s
|
||||
ignoredmediatypes:
|
||||
- application/octet-stream
|
||||
redis:
|
||||
@@ -268,7 +268,6 @@ compatibility:
|
||||
schema1:
|
||||
signingkeyfile: /etc/registry/key.json
|
||||
validation:
|
||||
enabled: true
|
||||
manifests:
|
||||
urls:
|
||||
allow:
|
||||
@@ -553,7 +552,7 @@ The `auth` option is **optional**. Possible auth providers include:
|
||||
|
||||
- [`silly`](#silly)
|
||||
- [`token`](#token)
|
||||
- [`htpasswd`](#token)
|
||||
- [`htpasswd`](#htpasswd)
|
||||
|
||||
You can configure only one authentication provider.
|
||||
|
||||
@@ -817,9 +816,9 @@ notifications:
|
||||
disabled: false
|
||||
url: https://my.listener.com/event
|
||||
headers: <http.Header>
|
||||
timeout: 500
|
||||
threshold: 5
|
||||
backoff: 1000
|
||||
timeout: 1s
|
||||
threshold: 10
|
||||
backoff: 1s
|
||||
ignoredmediatypes:
|
||||
- application/octet-stream
|
||||
```
|
||||
@@ -948,7 +947,7 @@ a file.
|
||||
| Parameter | Required | Description |
|
||||
|-----------|----------|-------------------------------------------------------|
|
||||
| `file` | yes | The path to check for existence of a file. |
|
||||
| `interval`| no | How long to wait before repeating the check. A positive integer and an optional suffix indicating the unit of time. The suffix is one of `ns`, `us`, `ms`, `s`, `m`, or `h`. Defaults to `10s` if the value is omitted. |
|
||||
| `interval`| no | How long to wait before repeating the check. A positive integer and an optional suffix indicating the unit of time. The suffix is one of `ns`, `us`, `ms`, `s`, `m`, or `h`. Defaults to `10s` if the value is omitted. If you specify a value but omit the suffix, the value is interpreted as a number of nanoseconds. |
|
||||
|
||||
### `http`
|
||||
|
||||
@@ -1028,7 +1027,6 @@ features. Each subsection defines such a feature with configurable behavior.
|
||||
|
||||
```none
|
||||
validation:
|
||||
enabled: true
|
||||
manifests:
|
||||
urls:
|
||||
allow:
|
||||
@@ -1037,14 +1035,15 @@ validation:
|
||||
- ^https?://www\.example\.com/
|
||||
```
|
||||
|
||||
### `enabled`
|
||||
### `disabled`
|
||||
|
||||
Use the `enabled` flag to enable the other options in the `validation`
|
||||
section. They are disabled by default.
|
||||
The `disabled` flag disables the other options in the `validation`
|
||||
section. They are enabled by default. This option deprecates the `enabled` flag.
|
||||
|
||||
### `manifests`
|
||||
|
||||
Use the `manifest` subsection to configure manifest validation.
|
||||
Use the `manifests` subsection to configure validation of manifests. If
|
||||
`disabled` is `false`, the validation allows nothing.
|
||||
|
||||
#### `urls`
|
||||
|
||||
@@ -1110,7 +1109,7 @@ middleware:
|
||||
baseurl: http://d111111abcdef8.cloudfront.net
|
||||
privatekey: /path/to/asecret.pem
|
||||
keypairid: asecret
|
||||
duration: 60
|
||||
duration: 60s
|
||||
```
|
||||
|
||||
See the configuration reference for [Cloudfront](#cloudfront) for more
|
||||
|
||||
2
vendor/github.com/docker/distribution/docs/spec/api.md
generated
vendored
2
vendor/github.com/docker/distribution/docs/spec/api.md
generated
vendored
@@ -795,7 +795,7 @@ Note that the upload url will not be available forever. If the upload uuid is
|
||||
unknown to the registry, a `404 Not Found` response will be returned and the
|
||||
client must restart the upload process.
|
||||
|
||||
### Deleting a Layer
|
||||
#### Deleting a Layer
|
||||
|
||||
A layer may be deleted from the registry via its `name` and `digest`. A
|
||||
delete may be issued with the following request format:
|
||||
|
||||
2
vendor/github.com/docker/distribution/docs/spec/api.md.tmpl
generated
vendored
2
vendor/github.com/docker/distribution/docs/spec/api.md.tmpl
generated
vendored
@@ -795,7 +795,7 @@ Note that the upload url will not be available forever. If the upload uuid is
|
||||
unknown to the registry, a `404 Not Found` response will be returned and the
|
||||
client must restart the upload process.
|
||||
|
||||
### Deleting a Layer
|
||||
#### Deleting a Layer
|
||||
|
||||
A layer may be deleted from the registry via its `name` and `digest`. A
|
||||
delete may be issued with the following request format:
|
||||
|
||||
2
vendor/github.com/docker/distribution/docs/spec/manifest-v2-1.md
generated
vendored
2
vendor/github.com/docker/distribution/docs/spec/manifest-v2-1.md
generated
vendored
@@ -6,7 +6,7 @@ keywords: ["registry, on-prem, images, tags, repository, distribution, api, adva
|
||||
|
||||
# Image Manifest Version 2, Schema 1
|
||||
|
||||
This document outlines the format of of the V2 image manifest. The image
|
||||
This document outlines the format of the V2 image manifest. The image
|
||||
manifest described herein was introduced in the Docker daemon in the [v1.3.0
|
||||
release](https://github.com/docker/docker/commit/9f482a66ab37ec396ac61ed0c00d59122ac07453).
|
||||
It is a provisional manifest to provide a compatibility with the [V1 Image
|
||||
|
||||
10
vendor/github.com/docker/distribution/docs/spec/manifest-v2-2.md
generated
vendored
10
vendor/github.com/docker/distribution/docs/spec/manifest-v2-2.md
generated
vendored
@@ -6,7 +6,7 @@ keywords: ["registry, on-prem, images, tags, repository, distribution, api, adva
|
||||
|
||||
# Image Manifest Version 2, Schema 2
|
||||
|
||||
This document outlines the format of of the V2 image manifest, schema version 2.
|
||||
This document outlines the format of the V2 image manifest, schema version 2.
|
||||
The original (and provisional) image manifest for V2 (schema 1), was introduced
|
||||
in the Docker daemon in the [v1.3.0
|
||||
release](https://github.com/docker/docker/commit/9f482a66ab37ec396ac61ed0c00d59122ac07453)
|
||||
@@ -60,8 +60,8 @@ image manifest based on the Content-Type returned in the HTTP response.
|
||||
- **`mediaType`** *string*
|
||||
|
||||
The MIME type of the referenced object. This will generally be
|
||||
`application/vnd.docker.image.manifest.v2+json`, but it could also
|
||||
be `application/vnd.docker.image.manifest.v1+json` if the manifest
|
||||
`application/vnd.docker.distribution.manifest.v2+json`, but it could also
|
||||
be `application/vnd.docker.distribution.manifest.v1+json` if the manifest
|
||||
list references a legacy schema-1 manifest.
|
||||
|
||||
- **`size`** *int*
|
||||
@@ -123,7 +123,7 @@ image manifest based on the Content-Type returned in the HTTP response.
|
||||
"mediaType": "application/vnd.docker.distribution.manifest.list.v2+json",
|
||||
"manifests": [
|
||||
{
|
||||
"mediaType": "application/vnd.docker.image.manifest.v2+json",
|
||||
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
|
||||
"size": 7143,
|
||||
"digest": "sha256:e692418e4cbaf90ca69d05a66403747baa33ee08806650b51fab815ad7fc331f",
|
||||
"platform": {
|
||||
@@ -132,7 +132,7 @@ image manifest based on the Content-Type returned in the HTTP response.
|
||||
}
|
||||
},
|
||||
{
|
||||
"mediaType": "application/vnd.docker.image.manifest.v2+json",
|
||||
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
|
||||
"size": 7682,
|
||||
"digest": "sha256:5b0bcabd1ed22e9fb1310cf6c2dec7cdef19f0ad69efa1f392e94a4333501270",
|
||||
"platform": {
|
||||
|
||||
4
vendor/github.com/docker/distribution/errors.go
generated
vendored
4
vendor/github.com/docker/distribution/errors.go
generated
vendored
@@ -5,7 +5,7 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/distribution/digest"
|
||||
"github.com/opencontainers/go-digest"
|
||||
)
|
||||
|
||||
// ErrAccessDenied is returned when an access to a requested resource is
|
||||
@@ -77,7 +77,7 @@ func (err ErrManifestUnknownRevision) Error() string {
|
||||
type ErrManifestUnverified struct{}
|
||||
|
||||
func (ErrManifestUnverified) Error() string {
|
||||
return fmt.Sprintf("unverified manifest")
|
||||
return "unverified manifest"
|
||||
}
|
||||
|
||||
// ErrManifestVerification provides a type to collect errors encountered
|
||||
|
||||
17
vendor/github.com/docker/distribution/health/checks/checks.go
generated
vendored
17
vendor/github.com/docker/distribution/health/checks/checks.go
generated
vendored
@@ -2,9 +2,11 @@ package checks
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
@@ -15,10 +17,19 @@ import (
|
||||
// if the file exists.
|
||||
func FileChecker(f string) health.Checker {
|
||||
return health.CheckFunc(func() error {
|
||||
if _, err := os.Stat(f); err == nil {
|
||||
return errors.New("file exists")
|
||||
absoluteFilePath, err := filepath.Abs(f)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get absolute path for %q: %v", f, err)
|
||||
}
|
||||
return nil
|
||||
|
||||
_, err = os.Stat(absoluteFilePath)
|
||||
if err == nil {
|
||||
return errors.New("file exists")
|
||||
} else if os.IsNotExist(err) {
|
||||
return nil
|
||||
}
|
||||
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
8
vendor/github.com/docker/distribution/health/doc.go
generated
vendored
8
vendor/github.com/docker/distribution/health/doc.go
generated
vendored
@@ -24,7 +24,7 @@
|
||||
// "manual" checks that allow the service to quickly be brought in/out of
|
||||
// rotation.
|
||||
//
|
||||
// import _ "github.com/docker/distribution/registry/health/api"
|
||||
// import _ "github.com/docker/distribution/health/api"
|
||||
//
|
||||
// # curl localhost:5001/debug/health
|
||||
// {}
|
||||
@@ -122,6 +122,12 @@
|
||||
// # curl localhost:5001/debug/health
|
||||
// {"fileChecker":"file exists"}
|
||||
//
|
||||
// FileChecker only accepts absolute or relative file path. It does not work
|
||||
// properly with tilde(~). You should make sure that the application has
|
||||
// proper permission(read and execute permission for directory along with
|
||||
// the specified file path). Otherwise, the FileChecker will report error
|
||||
// and file health check is not ok.
|
||||
//
|
||||
// You could also test the connectivity to a downstream service by using a
|
||||
// "HTTPChecker", but ensure that you only mark the test unhealthy if there
|
||||
// are a minimum of two failures in a row:
|
||||
|
||||
4
vendor/github.com/docker/distribution/health/health_test.go
generated
vendored
4
vendor/github.com/docker/distribution/health/health_test.go
generated
vendored
@@ -25,8 +25,8 @@ func TestReturns200IfThereAreNoChecks(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestReturns500IfThereAreErrorChecks ensures that the result code of the
|
||||
// health endpoint is 500 if there are health checks with errors
|
||||
// TestReturns503IfThereAreErrorChecks ensures that the result code of the
|
||||
// health endpoint is 503 if there are health checks with errors.
|
||||
func TestReturns503IfThereAreErrorChecks(t *testing.T) {
|
||||
recorder := httptest.NewRecorder()
|
||||
|
||||
|
||||
4
vendor/github.com/docker/distribution/manifest/manifestlist/manifestlist.go
generated
vendored
4
vendor/github.com/docker/distribution/manifest/manifestlist/manifestlist.go
generated
vendored
@@ -6,8 +6,8 @@ import (
|
||||
"fmt"
|
||||
|
||||
"github.com/docker/distribution"
|
||||
"github.com/docker/distribution/digest"
|
||||
"github.com/docker/distribution/manifest"
|
||||
"github.com/opencontainers/go-digest"
|
||||
)
|
||||
|
||||
// MediaTypeManifestList specifies the mediaType for manifest lists.
|
||||
@@ -81,7 +81,7 @@ type ManifestList struct {
|
||||
Manifests []ManifestDescriptor `json:"manifests"`
|
||||
}
|
||||
|
||||
// References returnes the distribution descriptors for the referenced image
|
||||
// References returns the distribution descriptors for the referenced image
|
||||
// manifests.
|
||||
func (m ManifestList) References() []distribution.Descriptor {
|
||||
dependencies := make([]distribution.Descriptor, len(m.Manifests))
|
||||
|
||||
11
vendor/github.com/docker/distribution/manifest/schema1/config_builder.go
generated
vendored
11
vendor/github.com/docker/distribution/manifest/schema1/config_builder.go
generated
vendored
@@ -9,10 +9,10 @@ import (
|
||||
|
||||
"github.com/docker/distribution"
|
||||
"github.com/docker/distribution/context"
|
||||
"github.com/docker/distribution/digest"
|
||||
"github.com/docker/distribution/manifest"
|
||||
"github.com/docker/distribution/reference"
|
||||
"github.com/docker/libtrust"
|
||||
"github.com/opencontainers/go-digest"
|
||||
)
|
||||
|
||||
type diffID digest.Digest
|
||||
@@ -240,8 +240,13 @@ func (mb *configManifestBuilder) emptyTar(ctx context.Context) (digest.Digest, e
|
||||
|
||||
// AppendReference adds a reference to the current ManifestBuilder
|
||||
func (mb *configManifestBuilder) AppendReference(d distribution.Describable) error {
|
||||
// todo: verification here?
|
||||
mb.descriptors = append(mb.descriptors, d.Descriptor())
|
||||
descriptor := d.Descriptor()
|
||||
|
||||
if err := descriptor.Digest.Validate(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
mb.descriptors = append(mb.descriptors, descriptor)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
8
vendor/github.com/docker/distribution/manifest/schema1/config_builder_test.go
generated
vendored
8
vendor/github.com/docker/distribution/manifest/schema1/config_builder_test.go
generated
vendored
@@ -9,9 +9,9 @@ import (
|
||||
|
||||
"github.com/docker/distribution"
|
||||
"github.com/docker/distribution/context"
|
||||
"github.com/docker/distribution/digest"
|
||||
"github.com/docker/distribution/reference"
|
||||
"github.com/docker/libtrust"
|
||||
"github.com/opencontainers/go-digest"
|
||||
)
|
||||
|
||||
type mockBlobService struct {
|
||||
@@ -197,10 +197,14 @@ func TestConfigBuilder(t *testing.T) {
|
||||
|
||||
bs := &mockBlobService{descriptors: make(map[digest.Digest]distribution.Descriptor)}
|
||||
|
||||
ref, err := reference.ParseNamed("testrepo:testtag")
|
||||
ref, err := reference.WithName("testrepo")
|
||||
if err != nil {
|
||||
t.Fatalf("could not parse reference: %v", err)
|
||||
}
|
||||
ref, err = reference.WithTag(ref, "testtag")
|
||||
if err != nil {
|
||||
t.Fatalf("could not add tag: %v", err)
|
||||
}
|
||||
|
||||
builder := NewConfigManifestBuilder(bs, pk, ref, []byte(imgJSON))
|
||||
|
||||
|
||||
2
vendor/github.com/docker/distribution/manifest/schema1/manifest.go
generated
vendored
2
vendor/github.com/docker/distribution/manifest/schema1/manifest.go
generated
vendored
@@ -5,9 +5,9 @@ import (
|
||||
"fmt"
|
||||
|
||||
"github.com/docker/distribution"
|
||||
"github.com/docker/distribution/digest"
|
||||
"github.com/docker/distribution/manifest"
|
||||
"github.com/docker/libtrust"
|
||||
"github.com/opencontainers/go-digest"
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
2
vendor/github.com/docker/distribution/manifest/schema1/reference_builder.go
generated
vendored
2
vendor/github.com/docker/distribution/manifest/schema1/reference_builder.go
generated
vendored
@@ -6,10 +6,10 @@ import (
|
||||
"errors"
|
||||
"github.com/docker/distribution"
|
||||
"github.com/docker/distribution/context"
|
||||
"github.com/docker/distribution/digest"
|
||||
"github.com/docker/distribution/manifest"
|
||||
"github.com/docker/distribution/reference"
|
||||
"github.com/docker/libtrust"
|
||||
"github.com/opencontainers/go-digest"
|
||||
)
|
||||
|
||||
// referenceManifestBuilder is a type for constructing manifests from schema1
|
||||
|
||||
4
vendor/github.com/docker/distribution/manifest/schema1/reference_builder_test.go
generated
vendored
4
vendor/github.com/docker/distribution/manifest/schema1/reference_builder_test.go
generated
vendored
@@ -4,10 +4,10 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/docker/distribution/context"
|
||||
"github.com/docker/distribution/digest"
|
||||
"github.com/docker/distribution/manifest"
|
||||
"github.com/docker/distribution/reference"
|
||||
"github.com/docker/libtrust"
|
||||
"github.com/opencontainers/go-digest"
|
||||
)
|
||||
|
||||
func makeSignedManifest(t *testing.T, pk libtrust.PrivateKey, refs []Reference) *SignedManifest {
|
||||
@@ -55,7 +55,7 @@ func TestReferenceBuilder(t *testing.T) {
|
||||
|
||||
handCrafted := makeSignedManifest(t, pk, []Reference{r1, r2})
|
||||
|
||||
ref, err := reference.ParseNamed(handCrafted.Manifest.Name)
|
||||
ref, err := reference.WithName(handCrafted.Manifest.Name)
|
||||
if err != nil {
|
||||
t.Fatalf("could not parse reference: %v", err)
|
||||
}
|
||||
|
||||
32
vendor/github.com/docker/distribution/manifest/schema2/builder.go
generated
vendored
32
vendor/github.com/docker/distribution/manifest/schema2/builder.go
generated
vendored
@@ -3,7 +3,7 @@ package schema2
|
||||
import (
|
||||
"github.com/docker/distribution"
|
||||
"github.com/docker/distribution/context"
|
||||
"github.com/docker/distribution/digest"
|
||||
"github.com/opencontainers/go-digest"
|
||||
)
|
||||
|
||||
// builder is a type for constructing manifests.
|
||||
@@ -11,21 +11,25 @@ type builder struct {
|
||||
// bs is a BlobService used to publish the configuration blob.
|
||||
bs distribution.BlobService
|
||||
|
||||
// configMediaType is media type used to describe configuration
|
||||
configMediaType string
|
||||
|
||||
// configJSON references
|
||||
configJSON []byte
|
||||
|
||||
// layers is a list of layer descriptors that gets built by successive
|
||||
// calls to AppendReference.
|
||||
layers []distribution.Descriptor
|
||||
// dependencies is a list of descriptors that gets built by successive
|
||||
// calls to AppendReference. In case of image configuration these are layers.
|
||||
dependencies []distribution.Descriptor
|
||||
}
|
||||
|
||||
// NewManifestBuilder is used to build new manifests for the current schema
|
||||
// version. It takes a BlobService so it can publish the configuration blob
|
||||
// as part of the Build process.
|
||||
func NewManifestBuilder(bs distribution.BlobService, configJSON []byte) distribution.ManifestBuilder {
|
||||
func NewManifestBuilder(bs distribution.BlobService, configMediaType string, configJSON []byte) distribution.ManifestBuilder {
|
||||
mb := &builder{
|
||||
bs: bs,
|
||||
configJSON: make([]byte, len(configJSON)),
|
||||
bs: bs,
|
||||
configMediaType: configMediaType,
|
||||
configJSON: make([]byte, len(configJSON)),
|
||||
}
|
||||
copy(mb.configJSON, configJSON)
|
||||
|
||||
@@ -36,9 +40,9 @@ func NewManifestBuilder(bs distribution.BlobService, configJSON []byte) distribu
|
||||
func (mb *builder) Build(ctx context.Context) (distribution.Manifest, error) {
|
||||
m := Manifest{
|
||||
Versioned: SchemaVersion,
|
||||
Layers: make([]distribution.Descriptor, len(mb.layers)),
|
||||
Layers: make([]distribution.Descriptor, len(mb.dependencies)),
|
||||
}
|
||||
copy(m.Layers, mb.layers)
|
||||
copy(m.Layers, mb.dependencies)
|
||||
|
||||
configDigest := digest.FromBytes(mb.configJSON)
|
||||
|
||||
@@ -48,7 +52,7 @@ func (mb *builder) Build(ctx context.Context) (distribution.Manifest, error) {
|
||||
case nil:
|
||||
// Override MediaType, since Put always replaces the specified media
|
||||
// type with application/octet-stream in the descriptor it returns.
|
||||
m.Config.MediaType = MediaTypeConfig
|
||||
m.Config.MediaType = mb.configMediaType
|
||||
return FromStruct(m)
|
||||
case distribution.ErrBlobUnknown:
|
||||
// nop
|
||||
@@ -57,10 +61,10 @@ func (mb *builder) Build(ctx context.Context) (distribution.Manifest, error) {
|
||||
}
|
||||
|
||||
// Add config to the blob store
|
||||
m.Config, err = mb.bs.Put(ctx, MediaTypeConfig, mb.configJSON)
|
||||
m.Config, err = mb.bs.Put(ctx, mb.configMediaType, mb.configJSON)
|
||||
// Override MediaType, since Put always replaces the specified media
|
||||
// type with application/octet-stream in the descriptor it returns.
|
||||
m.Config.MediaType = MediaTypeConfig
|
||||
m.Config.MediaType = mb.configMediaType
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -70,11 +74,11 @@ func (mb *builder) Build(ctx context.Context) (distribution.Manifest, error) {
|
||||
|
||||
// AppendReference adds a reference to the current ManifestBuilder.
|
||||
func (mb *builder) AppendReference(d distribution.Describable) error {
|
||||
mb.layers = append(mb.layers, d.Descriptor())
|
||||
mb.dependencies = append(mb.dependencies, d.Descriptor())
|
||||
return nil
|
||||
}
|
||||
|
||||
// References returns the current references added to this builder.
|
||||
func (mb *builder) References() []distribution.Descriptor {
|
||||
return mb.layers
|
||||
return mb.dependencies
|
||||
}
|
||||
|
||||
6
vendor/github.com/docker/distribution/manifest/schema2/builder_test.go
generated
vendored
6
vendor/github.com/docker/distribution/manifest/schema2/builder_test.go
generated
vendored
@@ -6,7 +6,7 @@ import (
|
||||
|
||||
"github.com/docker/distribution"
|
||||
"github.com/docker/distribution/context"
|
||||
"github.com/docker/distribution/digest"
|
||||
"github.com/opencontainers/go-digest"
|
||||
)
|
||||
|
||||
type mockBlobService struct {
|
||||
@@ -166,7 +166,7 @@ func TestBuilder(t *testing.T) {
|
||||
}
|
||||
|
||||
bs := &mockBlobService{descriptors: make(map[digest.Digest]distribution.Descriptor)}
|
||||
builder := NewManifestBuilder(bs, imgJSON)
|
||||
builder := NewManifestBuilder(bs, MediaTypeImageConfig, imgJSON)
|
||||
|
||||
for _, d := range descriptors {
|
||||
if err := builder.AppendReference(d); err != nil {
|
||||
@@ -195,7 +195,7 @@ func TestBuilder(t *testing.T) {
|
||||
if target.Digest != configDigest {
|
||||
t.Fatalf("unexpected digest in target: %s", target.Digest.String())
|
||||
}
|
||||
if target.MediaType != MediaTypeConfig {
|
||||
if target.MediaType != MediaTypeImageConfig {
|
||||
t.Fatalf("unexpected media type in target: %s", target.MediaType)
|
||||
}
|
||||
if target.Size != 3153 {
|
||||
|
||||
10
vendor/github.com/docker/distribution/manifest/schema2/manifest.go
generated
vendored
10
vendor/github.com/docker/distribution/manifest/schema2/manifest.go
generated
vendored
@@ -6,16 +6,16 @@ import (
|
||||
"fmt"
|
||||
|
||||
"github.com/docker/distribution"
|
||||
"github.com/docker/distribution/digest"
|
||||
"github.com/docker/distribution/manifest"
|
||||
"github.com/opencontainers/go-digest"
|
||||
)
|
||||
|
||||
const (
|
||||
// MediaTypeManifest specifies the mediaType for the current version.
|
||||
MediaTypeManifest = "application/vnd.docker.distribution.manifest.v2+json"
|
||||
|
||||
// MediaTypeConfig specifies the mediaType for the image configuration.
|
||||
MediaTypeConfig = "application/vnd.docker.container.image.v1+json"
|
||||
// MediaTypeImageConfig specifies the mediaType for the image configuration.
|
||||
MediaTypeImageConfig = "application/vnd.docker.container.image.v1+json"
|
||||
|
||||
// MediaTypePluginConfig specifies the mediaType for plugin configuration.
|
||||
MediaTypePluginConfig = "application/vnd.docker.plugin.v1+json"
|
||||
@@ -27,6 +27,10 @@ const (
|
||||
// MediaTypeForeignLayer is the mediaType used for layers that must be
|
||||
// downloaded from foreign URLs.
|
||||
MediaTypeForeignLayer = "application/vnd.docker.image.rootfs.foreign.diff.tar.gzip"
|
||||
|
||||
// MediaTypeUncompressedLayer is the mediaType used for layers which
|
||||
// are not compressed.
|
||||
MediaTypeUncompressedLayer = "application/vnd.docker.image.rootfs.diff.tar"
|
||||
)
|
||||
|
||||
var (
|
||||
|
||||
4
vendor/github.com/docker/distribution/manifest/schema2/manifest_test.go
generated
vendored
4
vendor/github.com/docker/distribution/manifest/schema2/manifest_test.go
generated
vendored
@@ -32,7 +32,7 @@ func TestManifest(t *testing.T) {
|
||||
Config: distribution.Descriptor{
|
||||
Digest: "sha256:1a9ec845ee94c202b2d5da74a24f0ed2058318bfa9879fa541efaecba272e86b",
|
||||
Size: 985,
|
||||
MediaType: MediaTypeConfig,
|
||||
MediaType: MediaTypeImageConfig,
|
||||
},
|
||||
Layers: []distribution.Descriptor{
|
||||
{
|
||||
@@ -82,7 +82,7 @@ func TestManifest(t *testing.T) {
|
||||
if target.Digest != "sha256:1a9ec845ee94c202b2d5da74a24f0ed2058318bfa9879fa541efaecba272e86b" {
|
||||
t.Fatalf("unexpected digest in target: %s", target.Digest.String())
|
||||
}
|
||||
if target.MediaType != MediaTypeConfig {
|
||||
if target.MediaType != MediaTypeImageConfig {
|
||||
t.Fatalf("unexpected media type in target: %s", target.MediaType)
|
||||
}
|
||||
if target.Size != 985 {
|
||||
|
||||
22
vendor/github.com/docker/distribution/manifests.go
generated
vendored
22
vendor/github.com/docker/distribution/manifests.go
generated
vendored
@@ -5,7 +5,7 @@ import (
|
||||
"mime"
|
||||
|
||||
"github.com/docker/distribution/context"
|
||||
"github.com/docker/distribution/digest"
|
||||
"github.com/opencontainers/go-digest"
|
||||
)
|
||||
|
||||
// Manifest represents a registry object specifying a set of
|
||||
@@ -22,8 +22,8 @@ type Manifest interface {
|
||||
References() []Descriptor
|
||||
|
||||
// Payload provides the serialized format of the manifest, in addition to
|
||||
// the mediatype.
|
||||
Payload() (mediatype string, payload []byte, err error)
|
||||
// the media type.
|
||||
Payload() (mediaType string, payload []byte, err error)
|
||||
}
|
||||
|
||||
// ManifestBuilder creates a manifest allowing one to include dependencies.
|
||||
@@ -94,20 +94,20 @@ var mappings = make(map[string]UnmarshalFunc, 0)
|
||||
func UnmarshalManifest(ctHeader string, p []byte) (Manifest, Descriptor, error) {
|
||||
// Need to look up by the actual media type, not the raw contents of
|
||||
// the header. Strip semicolons and anything following them.
|
||||
var mediatype string
|
||||
var mediaType string
|
||||
if ctHeader != "" {
|
||||
var err error
|
||||
mediatype, _, err = mime.ParseMediaType(ctHeader)
|
||||
mediaType, _, err = mime.ParseMediaType(ctHeader)
|
||||
if err != nil {
|
||||
return nil, Descriptor{}, err
|
||||
}
|
||||
}
|
||||
|
||||
unmarshalFunc, ok := mappings[mediatype]
|
||||
unmarshalFunc, ok := mappings[mediaType]
|
||||
if !ok {
|
||||
unmarshalFunc, ok = mappings[""]
|
||||
if !ok {
|
||||
return nil, Descriptor{}, fmt.Errorf("unsupported manifest mediatype and no default available: %s", mediatype)
|
||||
return nil, Descriptor{}, fmt.Errorf("unsupported manifest media type and no default available: %s", mediaType)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -116,10 +116,10 @@ func UnmarshalManifest(ctHeader string, p []byte) (Manifest, Descriptor, error)
|
||||
|
||||
// RegisterManifestSchema registers an UnmarshalFunc for a given schema type. This
|
||||
// should be called from specific
|
||||
func RegisterManifestSchema(mediatype string, u UnmarshalFunc) error {
|
||||
if _, ok := mappings[mediatype]; ok {
|
||||
return fmt.Errorf("manifest mediatype registration would overwrite existing: %s", mediatype)
|
||||
func RegisterManifestSchema(mediaType string, u UnmarshalFunc) error {
|
||||
if _, ok := mappings[mediaType]; ok {
|
||||
return fmt.Errorf("manifest media type registration would overwrite existing: %s", mediaType)
|
||||
}
|
||||
mappings[mediatype] = u
|
||||
mappings[mediaType] = u
|
||||
return nil
|
||||
}
|
||||
|
||||
2
vendor/github.com/docker/distribution/notifications/bridge.go
generated
vendored
2
vendor/github.com/docker/distribution/notifications/bridge.go
generated
vendored
@@ -6,9 +6,9 @@ import (
|
||||
|
||||
"github.com/docker/distribution"
|
||||
"github.com/docker/distribution/context"
|
||||
"github.com/docker/distribution/digest"
|
||||
"github.com/docker/distribution/reference"
|
||||
"github.com/docker/distribution/uuid"
|
||||
"github.com/opencontainers/go-digest"
|
||||
)
|
||||
|
||||
type bridge struct {
|
||||
|
||||
14
vendor/github.com/docker/distribution/notifications/bridge_test.go
generated
vendored
14
vendor/github.com/docker/distribution/notifications/bridge_test.go
generated
vendored
@@ -4,12 +4,12 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/docker/distribution"
|
||||
"github.com/docker/distribution/digest"
|
||||
"github.com/docker/distribution/manifest/schema1"
|
||||
"github.com/docker/distribution/reference"
|
||||
"github.com/docker/distribution/registry/api/v2"
|
||||
"github.com/docker/distribution/uuid"
|
||||
"github.com/docker/libtrust"
|
||||
"github.com/opencontainers/go-digest"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -43,7 +43,7 @@ func TestEventBridgeManifestPulled(t *testing.T) {
|
||||
return nil
|
||||
}))
|
||||
|
||||
repoRef, _ := reference.ParseNamed(repo)
|
||||
repoRef, _ := reference.WithName(repo)
|
||||
if err := l.ManifestPulled(repoRef, sm); err != nil {
|
||||
t.Fatalf("unexpected error notifying manifest pull: %v", err)
|
||||
}
|
||||
@@ -56,7 +56,7 @@ func TestEventBridgeManifestPushed(t *testing.T) {
|
||||
return nil
|
||||
}))
|
||||
|
||||
repoRef, _ := reference.ParseNamed(repo)
|
||||
repoRef, _ := reference.WithName(repo)
|
||||
if err := l.ManifestPushed(repoRef, sm); err != nil {
|
||||
t.Fatalf("unexpected error notifying manifest pull: %v", err)
|
||||
}
|
||||
@@ -72,7 +72,7 @@ func TestEventBridgeManifestPushedWithTag(t *testing.T) {
|
||||
return nil
|
||||
}))
|
||||
|
||||
repoRef, _ := reference.ParseNamed(repo)
|
||||
repoRef, _ := reference.WithName(repo)
|
||||
if err := l.ManifestPushed(repoRef, sm, distribution.WithTag(m.Tag)); err != nil {
|
||||
t.Fatalf("unexpected error notifying manifest pull: %v", err)
|
||||
}
|
||||
@@ -88,7 +88,7 @@ func TestEventBridgeManifestPulledWithTag(t *testing.T) {
|
||||
return nil
|
||||
}))
|
||||
|
||||
repoRef, _ := reference.ParseNamed(repo)
|
||||
repoRef, _ := reference.WithName(repo)
|
||||
if err := l.ManifestPulled(repoRef, sm, distribution.WithTag(m.Tag)); err != nil {
|
||||
t.Fatalf("unexpected error notifying manifest pull: %v", err)
|
||||
}
|
||||
@@ -100,7 +100,7 @@ func TestEventBridgeManifestDeleted(t *testing.T) {
|
||||
return nil
|
||||
}))
|
||||
|
||||
repoRef, _ := reference.ParseNamed(repo)
|
||||
repoRef, _ := reference.WithName(repo)
|
||||
if err := l.ManifestDeleted(repoRef, dgst); err != nil {
|
||||
t.Fatalf("unexpected error notifying manifest pull: %v", err)
|
||||
}
|
||||
@@ -160,7 +160,7 @@ func checkCommonManifest(t *testing.T, action string, events ...Event) {
|
||||
t.Fatalf("unexpected event action: %q != %q", event.Action, action)
|
||||
}
|
||||
|
||||
repoRef, _ := reference.ParseNamed(repo)
|
||||
repoRef, _ := reference.WithName(repo)
|
||||
ref, _ := reference.WithDigest(repoRef, dgst)
|
||||
u, err := ub.BuildManifestURL(ref)
|
||||
if err != nil {
|
||||
|
||||
2
vendor/github.com/docker/distribution/notifications/endpoint.go
generated
vendored
2
vendor/github.com/docker/distribution/notifications/endpoint.go
generated
vendored
@@ -13,7 +13,7 @@ type EndpointConfig struct {
|
||||
Threshold int
|
||||
Backoff time.Duration
|
||||
IgnoredMediaTypes []string
|
||||
Transport *http.Transport
|
||||
Transport *http.Transport `json:"-"`
|
||||
}
|
||||
|
||||
// defaults set any zero-valued fields to a reasonable default.
|
||||
|
||||
4
vendor/github.com/docker/distribution/notifications/event.go
generated
vendored
4
vendor/github.com/docker/distribution/notifications/event.go
generated
vendored
@@ -77,7 +77,7 @@ type Event struct {
|
||||
Request RequestRecord `json:"request,omitempty"`
|
||||
|
||||
// Actor specifies the agent that initiated the event. For most
|
||||
// situations, this could be from the authorizaton context of the request.
|
||||
// situations, this could be from the authorization context of the request.
|
||||
Actor ActorRecord `json:"actor,omitempty"`
|
||||
|
||||
// Source identifies the registry node that generated the event. Put
|
||||
@@ -87,7 +87,7 @@ type Event struct {
|
||||
}
|
||||
|
||||
// ActorRecord specifies the agent that initiated the event. For most
|
||||
// situations, this could be from the authorizaton context of the request.
|
||||
// situations, this could be from the authorization context of the request.
|
||||
// Data in this record can refer to both the initiating client and the
|
||||
// generating request.
|
||||
type ActorRecord struct {
|
||||
|
||||
20
vendor/github.com/docker/distribution/notifications/http_test.go
generated
vendored
20
vendor/github.com/docker/distribution/notifications/http_test.go
generated
vendored
@@ -5,6 +5,7 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"mime"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"reflect"
|
||||
@@ -94,6 +95,21 @@ func TestHTTPSink(t *testing.T) {
|
||||
var expectedMetrics EndpointMetrics
|
||||
expectedMetrics.Statuses = make(map[string]int)
|
||||
|
||||
closeL, err := net.Listen("tcp", "localhost:0")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error creating listener: %v", err)
|
||||
}
|
||||
defer closeL.Close()
|
||||
go func() {
|
||||
for {
|
||||
c, err := closeL.Accept()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
c.Close()
|
||||
}
|
||||
}()
|
||||
|
||||
for _, tc := range []struct {
|
||||
events []Event // events to send
|
||||
url string
|
||||
@@ -121,8 +137,8 @@ func TestHTTPSink(t *testing.T) {
|
||||
failure: true,
|
||||
},
|
||||
{
|
||||
// Case where connection never goes through.
|
||||
url: "http://shoudlntresolve/",
|
||||
// Case where connection is immediately closed
|
||||
url: closeL.Addr().String(),
|
||||
failure: true,
|
||||
},
|
||||
} {
|
||||
|
||||
2
vendor/github.com/docker/distribution/notifications/listener.go
generated
vendored
2
vendor/github.com/docker/distribution/notifications/listener.go
generated
vendored
@@ -5,8 +5,8 @@ import (
|
||||
|
||||
"github.com/docker/distribution"
|
||||
"github.com/docker/distribution/context"
|
||||
"github.com/docker/distribution/digest"
|
||||
"github.com/docker/distribution/reference"
|
||||
"github.com/opencontainers/go-digest"
|
||||
)
|
||||
|
||||
// ManifestListener describes a set of methods for listening to events related to manifests.
|
||||
|
||||
4
vendor/github.com/docker/distribution/notifications/listener_test.go
generated
vendored
4
vendor/github.com/docker/distribution/notifications/listener_test.go
generated
vendored
@@ -7,7 +7,6 @@ import (
|
||||
|
||||
"github.com/docker/distribution"
|
||||
"github.com/docker/distribution/context"
|
||||
"github.com/docker/distribution/digest"
|
||||
"github.com/docker/distribution/manifest"
|
||||
"github.com/docker/distribution/manifest/schema1"
|
||||
"github.com/docker/distribution/reference"
|
||||
@@ -16,6 +15,7 @@ import (
|
||||
"github.com/docker/distribution/registry/storage/driver/inmemory"
|
||||
"github.com/docker/distribution/testutil"
|
||||
"github.com/docker/libtrust"
|
||||
"github.com/opencontainers/go-digest"
|
||||
)
|
||||
|
||||
func TestListener(t *testing.T) {
|
||||
@@ -33,7 +33,7 @@ func TestListener(t *testing.T) {
|
||||
ops: make(map[string]int),
|
||||
}
|
||||
|
||||
repoRef, _ := reference.ParseNamed("foo/bar")
|
||||
repoRef, _ := reference.WithName("foo/bar")
|
||||
repository, err := registry.Repository(ctx, repoRef)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error getting repo: %v", err)
|
||||
|
||||
28
vendor/github.com/docker/distribution/notifications/metrics_test.go
generated
vendored
Normal file
28
vendor/github.com/docker/distribution/notifications/metrics_test.go
generated
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
package notifications
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"expvar"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestMetricsExpvar(t *testing.T) {
|
||||
endpointsVar := expvar.Get("registry").(*expvar.Map).Get("notifications").(*expvar.Map).Get("endpoints")
|
||||
|
||||
var v interface{}
|
||||
if err := json.Unmarshal([]byte(endpointsVar.String()), &v); err != nil {
|
||||
t.Fatalf("unexpected error unmarshaling endpoints: %v", err)
|
||||
}
|
||||
if v != nil {
|
||||
t.Fatalf("expected nil, got %#v", v)
|
||||
}
|
||||
|
||||
NewEndpoint("x", "y", EndpointConfig{})
|
||||
|
||||
if err := json.Unmarshal([]byte(endpointsVar.String()), &v); err != nil {
|
||||
t.Fatalf("unexpected error unmarshaling endpoints: %v", err)
|
||||
}
|
||||
if slice, ok := v.([]interface{}); !ok || len(slice) != 1 {
|
||||
t.Logf("expected one-element []interface{}, got %#v", v)
|
||||
}
|
||||
}
|
||||
2
vendor/github.com/docker/distribution/notifications/sinks.go
generated
vendored
2
vendor/github.com/docker/distribution/notifications/sinks.go
generated
vendored
@@ -151,7 +151,7 @@ func (eq *eventQueue) Write(events ...Event) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Close shutsdown the event queue, flushing
|
||||
// Close shuts down the event queue, flushing
|
||||
func (eq *eventQueue) Close() error {
|
||||
eq.mu.Lock()
|
||||
defer eq.mu.Unlock()
|
||||
|
||||
42
vendor/github.com/docker/distribution/reference/helpers.go
generated
vendored
Normal file
42
vendor/github.com/docker/distribution/reference/helpers.go
generated
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
package reference
|
||||
|
||||
import "path"
|
||||
|
||||
// IsNameOnly returns true if reference only contains a repo name.
|
||||
func IsNameOnly(ref Named) bool {
|
||||
if _, ok := ref.(NamedTagged); ok {
|
||||
return false
|
||||
}
|
||||
if _, ok := ref.(Canonical); ok {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// FamiliarName returns the familiar name string
|
||||
// for the given named, familiarizing if needed.
|
||||
func FamiliarName(ref Named) string {
|
||||
if nn, ok := ref.(normalizedNamed); ok {
|
||||
return nn.Familiar().Name()
|
||||
}
|
||||
return ref.Name()
|
||||
}
|
||||
|
||||
// FamiliarString returns the familiar string representation
|
||||
// for the given reference, familiarizing if needed.
|
||||
func FamiliarString(ref Reference) string {
|
||||
if nn, ok := ref.(normalizedNamed); ok {
|
||||
return nn.Familiar().String()
|
||||
}
|
||||
return ref.String()
|
||||
}
|
||||
|
||||
// FamiliarMatch reports whether ref matches the specified pattern.
|
||||
// See https://godoc.org/path#Match for supported patterns.
|
||||
func FamiliarMatch(pattern string, ref Reference) (bool, error) {
|
||||
matched, err := path.Match(pattern, FamiliarString(ref))
|
||||
if namedRef, isNamed := ref.(Named); isNamed && !matched {
|
||||
matched, _ = path.Match(pattern, FamiliarName(namedRef))
|
||||
}
|
||||
return matched, err
|
||||
}
|
||||
170
vendor/github.com/docker/distribution/reference/normalize.go
generated
vendored
Normal file
170
vendor/github.com/docker/distribution/reference/normalize.go
generated
vendored
Normal file
@@ -0,0 +1,170 @@
|
||||
package reference
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/distribution/digestset"
|
||||
"github.com/opencontainers/go-digest"
|
||||
)
|
||||
|
||||
var (
|
||||
legacyDefaultDomain = "index.docker.io"
|
||||
defaultDomain = "docker.io"
|
||||
officialRepoName = "library"
|
||||
defaultTag = "latest"
|
||||
)
|
||||
|
||||
// normalizedNamed represents a name which has been
|
||||
// normalized and has a familiar form. A familiar name
|
||||
// is what is used in Docker UI. An example normalized
|
||||
// name is "docker.io/library/ubuntu" and corresponding
|
||||
// familiar name of "ubuntu".
|
||||
type normalizedNamed interface {
|
||||
Named
|
||||
Familiar() Named
|
||||
}
|
||||
|
||||
// ParseNormalizedNamed parses a string into a named reference
|
||||
// transforming a familiar name from Docker UI to a fully
|
||||
// qualified reference. If the value may be an identifier
|
||||
// use ParseAnyReference.
|
||||
func ParseNormalizedNamed(s string) (Named, error) {
|
||||
if ok := anchoredIdentifierRegexp.MatchString(s); ok {
|
||||
return nil, fmt.Errorf("invalid repository name (%s), cannot specify 64-byte hexadecimal strings", s)
|
||||
}
|
||||
domain, remainder := splitDockerDomain(s)
|
||||
var remoteName string
|
||||
if tagSep := strings.IndexRune(remainder, ':'); tagSep > -1 {
|
||||
remoteName = remainder[:tagSep]
|
||||
} else {
|
||||
remoteName = remainder
|
||||
}
|
||||
if strings.ToLower(remoteName) != remoteName {
|
||||
return nil, errors.New("invalid reference format: repository name must be lowercase")
|
||||
}
|
||||
|
||||
ref, err := Parse(domain + "/" + remainder)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
named, isNamed := ref.(Named)
|
||||
if !isNamed {
|
||||
return nil, fmt.Errorf("reference %s has no name", ref.String())
|
||||
}
|
||||
return named, nil
|
||||
}
|
||||
|
||||
// splitDockerDomain splits a repository name to domain and remotename string.
|
||||
// If no valid domain is found, the default domain is used. Repository name
|
||||
// needs to be already validated before.
|
||||
func splitDockerDomain(name string) (domain, remainder string) {
|
||||
i := strings.IndexRune(name, '/')
|
||||
if i == -1 || (!strings.ContainsAny(name[:i], ".:") && name[:i] != "localhost") {
|
||||
domain, remainder = defaultDomain, name
|
||||
} else {
|
||||
domain, remainder = name[:i], name[i+1:]
|
||||
}
|
||||
if domain == legacyDefaultDomain {
|
||||
domain = defaultDomain
|
||||
}
|
||||
if domain == defaultDomain && !strings.ContainsRune(remainder, '/') {
|
||||
remainder = officialRepoName + "/" + remainder
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// familiarizeName returns a shortened version of the name familiar
|
||||
// to to the Docker UI. Familiar names have the default domain
|
||||
// "docker.io" and "library/" repository prefix removed.
|
||||
// For example, "docker.io/library/redis" will have the familiar
|
||||
// name "redis" and "docker.io/dmcgowan/myapp" will be "dmcgowan/myapp".
|
||||
// Returns a familiarized named only reference.
|
||||
func familiarizeName(named namedRepository) repository {
|
||||
repo := repository{
|
||||
domain: named.Domain(),
|
||||
path: named.Path(),
|
||||
}
|
||||
|
||||
if repo.domain == defaultDomain {
|
||||
repo.domain = ""
|
||||
// Handle official repositories which have the pattern "library/<official repo name>"
|
||||
if split := strings.Split(repo.path, "/"); len(split) == 2 && split[0] == officialRepoName {
|
||||
repo.path = split[1]
|
||||
}
|
||||
}
|
||||
return repo
|
||||
}
|
||||
|
||||
func (r reference) Familiar() Named {
|
||||
return reference{
|
||||
namedRepository: familiarizeName(r.namedRepository),
|
||||
tag: r.tag,
|
||||
digest: r.digest,
|
||||
}
|
||||
}
|
||||
|
||||
func (r repository) Familiar() Named {
|
||||
return familiarizeName(r)
|
||||
}
|
||||
|
||||
func (t taggedReference) Familiar() Named {
|
||||
return taggedReference{
|
||||
namedRepository: familiarizeName(t.namedRepository),
|
||||
tag: t.tag,
|
||||
}
|
||||
}
|
||||
|
||||
func (c canonicalReference) Familiar() Named {
|
||||
return canonicalReference{
|
||||
namedRepository: familiarizeName(c.namedRepository),
|
||||
digest: c.digest,
|
||||
}
|
||||
}
|
||||
|
||||
// TagNameOnly adds the default tag "latest" to a reference if it only has
|
||||
// a repo name.
|
||||
func TagNameOnly(ref Named) Named {
|
||||
if IsNameOnly(ref) {
|
||||
namedTagged, err := WithTag(ref, defaultTag)
|
||||
if err != nil {
|
||||
// Default tag must be valid, to create a NamedTagged
|
||||
// type with non-validated input the WithTag function
|
||||
// should be used instead
|
||||
panic(err)
|
||||
}
|
||||
return namedTagged
|
||||
}
|
||||
return ref
|
||||
}
|
||||
|
||||
// ParseAnyReference parses a reference string as a possible identifier,
|
||||
// full digest, or familiar name.
|
||||
func ParseAnyReference(ref string) (Reference, error) {
|
||||
if ok := anchoredIdentifierRegexp.MatchString(ref); ok {
|
||||
return digestReference("sha256:" + ref), nil
|
||||
}
|
||||
if dgst, err := digest.Parse(ref); err == nil {
|
||||
return digestReference(dgst), nil
|
||||
}
|
||||
|
||||
return ParseNormalizedNamed(ref)
|
||||
}
|
||||
|
||||
// ParseAnyReferenceWithSet parses a reference string as a possible short
|
||||
// identifier to be matched in a digest set, a full digest, or familiar name.
|
||||
func ParseAnyReferenceWithSet(ref string, ds *digestset.Set) (Reference, error) {
|
||||
if ok := anchoredShortIdentifierRegexp.MatchString(ref); ok {
|
||||
dgst, err := ds.Lookup(ref)
|
||||
if err == nil {
|
||||
return digestReference(dgst), nil
|
||||
}
|
||||
} else {
|
||||
if dgst, err := digest.Parse(ref); err == nil {
|
||||
return digestReference(dgst), nil
|
||||
}
|
||||
}
|
||||
|
||||
return ParseNormalizedNamed(ref)
|
||||
}
|
||||
625
vendor/github.com/docker/distribution/reference/normalize_test.go
generated
vendored
Normal file
625
vendor/github.com/docker/distribution/reference/normalize_test.go
generated
vendored
Normal file
@@ -0,0 +1,625 @@
|
||||
package reference
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/distribution/digestset"
|
||||
"github.com/opencontainers/go-digest"
|
||||
)
|
||||
|
||||
func TestValidateReferenceName(t *testing.T) {
|
||||
validRepoNames := []string{
|
||||
"docker/docker",
|
||||
"library/debian",
|
||||
"debian",
|
||||
"docker.io/docker/docker",
|
||||
"docker.io/library/debian",
|
||||
"docker.io/debian",
|
||||
"index.docker.io/docker/docker",
|
||||
"index.docker.io/library/debian",
|
||||
"index.docker.io/debian",
|
||||
"127.0.0.1:5000/docker/docker",
|
||||
"127.0.0.1:5000/library/debian",
|
||||
"127.0.0.1:5000/debian",
|
||||
"thisisthesongthatneverendsitgoesonandonandonthisisthesongthatnev",
|
||||
|
||||
// This test case was moved from invalid to valid since it is valid input
|
||||
// when specified with a hostname, it removes the ambiguity from about
|
||||
// whether the value is an identifier or repository name
|
||||
"docker.io/1a3f5e7d9c1b3a5f7e9d1c3b5a7f9e1d3c5b7a9f1e3d5d7c9b1a3f5e7d9c1b3a",
|
||||
}
|
||||
invalidRepoNames := []string{
|
||||
"https://github.com/docker/docker",
|
||||
"docker/Docker",
|
||||
"-docker",
|
||||
"-docker/docker",
|
||||
"-docker.io/docker/docker",
|
||||
"docker///docker",
|
||||
"docker.io/docker/Docker",
|
||||
"docker.io/docker///docker",
|
||||
"1a3f5e7d9c1b3a5f7e9d1c3b5a7f9e1d3c5b7a9f1e3d5d7c9b1a3f5e7d9c1b3a",
|
||||
}
|
||||
|
||||
for _, name := range invalidRepoNames {
|
||||
_, err := ParseNormalizedNamed(name)
|
||||
if err == nil {
|
||||
t.Fatalf("Expected invalid repo name for %q", name)
|
||||
}
|
||||
}
|
||||
|
||||
for _, name := range validRepoNames {
|
||||
_, err := ParseNormalizedNamed(name)
|
||||
if err != nil {
|
||||
t.Fatalf("Error parsing repo name %s, got: %q", name, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateRemoteName(t *testing.T) {
|
||||
validRepositoryNames := []string{
|
||||
// Sanity check.
|
||||
"docker/docker",
|
||||
|
||||
// Allow 64-character non-hexadecimal names (hexadecimal names are forbidden).
|
||||
"thisisthesongthatneverendsitgoesonandonandonthisisthesongthatnev",
|
||||
|
||||
// Allow embedded hyphens.
|
||||
"docker-rules/docker",
|
||||
|
||||
// Allow multiple hyphens as well.
|
||||
"docker---rules/docker",
|
||||
|
||||
//Username doc and image name docker being tested.
|
||||
"doc/docker",
|
||||
|
||||
// single character names are now allowed.
|
||||
"d/docker",
|
||||
"jess/t",
|
||||
|
||||
// Consecutive underscores.
|
||||
"dock__er/docker",
|
||||
}
|
||||
for _, repositoryName := range validRepositoryNames {
|
||||
_, err := ParseNormalizedNamed(repositoryName)
|
||||
if err != nil {
|
||||
t.Errorf("Repository name should be valid: %v. Error: %v", repositoryName, err)
|
||||
}
|
||||
}
|
||||
|
||||
invalidRepositoryNames := []string{
|
||||
// Disallow capital letters.
|
||||
"docker/Docker",
|
||||
|
||||
// Only allow one slash.
|
||||
"docker///docker",
|
||||
|
||||
// Disallow 64-character hexadecimal.
|
||||
"1a3f5e7d9c1b3a5f7e9d1c3b5a7f9e1d3c5b7a9f1e3d5d7c9b1a3f5e7d9c1b3a",
|
||||
|
||||
// Disallow leading and trailing hyphens in namespace.
|
||||
"-docker/docker",
|
||||
"docker-/docker",
|
||||
"-docker-/docker",
|
||||
|
||||
// Don't allow underscores everywhere (as opposed to hyphens).
|
||||
"____/____",
|
||||
|
||||
"_docker/_docker",
|
||||
|
||||
// Disallow consecutive periods.
|
||||
"dock..er/docker",
|
||||
"dock_.er/docker",
|
||||
"dock-.er/docker",
|
||||
|
||||
// No repository.
|
||||
"docker/",
|
||||
|
||||
//namespace too long
|
||||
"this_is_not_a_valid_namespace_because_its_lenth_is_greater_than_255_this_is_not_a_valid_namespace_because_its_lenth_is_greater_than_255_this_is_not_a_valid_namespace_because_its_lenth_is_greater_than_255_this_is_not_a_valid_namespace_because_its_lenth_is_greater_than_255/docker",
|
||||
}
|
||||
for _, repositoryName := range invalidRepositoryNames {
|
||||
if _, err := ParseNormalizedNamed(repositoryName); err == nil {
|
||||
t.Errorf("Repository name should be invalid: %v", repositoryName)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseRepositoryInfo(t *testing.T) {
|
||||
type tcase struct {
|
||||
RemoteName, FamiliarName, FullName, AmbiguousName, Domain string
|
||||
}
|
||||
|
||||
tcases := []tcase{
|
||||
{
|
||||
RemoteName: "fooo/bar",
|
||||
FamiliarName: "fooo/bar",
|
||||
FullName: "docker.io/fooo/bar",
|
||||
AmbiguousName: "index.docker.io/fooo/bar",
|
||||
Domain: "docker.io",
|
||||
},
|
||||
{
|
||||
RemoteName: "library/ubuntu",
|
||||
FamiliarName: "ubuntu",
|
||||
FullName: "docker.io/library/ubuntu",
|
||||
AmbiguousName: "library/ubuntu",
|
||||
Domain: "docker.io",
|
||||
},
|
||||
{
|
||||
RemoteName: "nonlibrary/ubuntu",
|
||||
FamiliarName: "nonlibrary/ubuntu",
|
||||
FullName: "docker.io/nonlibrary/ubuntu",
|
||||
AmbiguousName: "",
|
||||
Domain: "docker.io",
|
||||
},
|
||||
{
|
||||
RemoteName: "other/library",
|
||||
FamiliarName: "other/library",
|
||||
FullName: "docker.io/other/library",
|
||||
AmbiguousName: "",
|
||||
Domain: "docker.io",
|
||||
},
|
||||
{
|
||||
RemoteName: "private/moonbase",
|
||||
FamiliarName: "127.0.0.1:8000/private/moonbase",
|
||||
FullName: "127.0.0.1:8000/private/moonbase",
|
||||
AmbiguousName: "",
|
||||
Domain: "127.0.0.1:8000",
|
||||
},
|
||||
{
|
||||
RemoteName: "privatebase",
|
||||
FamiliarName: "127.0.0.1:8000/privatebase",
|
||||
FullName: "127.0.0.1:8000/privatebase",
|
||||
AmbiguousName: "",
|
||||
Domain: "127.0.0.1:8000",
|
||||
},
|
||||
{
|
||||
RemoteName: "private/moonbase",
|
||||
FamiliarName: "example.com/private/moonbase",
|
||||
FullName: "example.com/private/moonbase",
|
||||
AmbiguousName: "",
|
||||
Domain: "example.com",
|
||||
},
|
||||
{
|
||||
RemoteName: "privatebase",
|
||||
FamiliarName: "example.com/privatebase",
|
||||
FullName: "example.com/privatebase",
|
||||
AmbiguousName: "",
|
||||
Domain: "example.com",
|
||||
},
|
||||
{
|
||||
RemoteName: "private/moonbase",
|
||||
FamiliarName: "example.com:8000/private/moonbase",
|
||||
FullName: "example.com:8000/private/moonbase",
|
||||
AmbiguousName: "",
|
||||
Domain: "example.com:8000",
|
||||
},
|
||||
{
|
||||
RemoteName: "privatebasee",
|
||||
FamiliarName: "example.com:8000/privatebasee",
|
||||
FullName: "example.com:8000/privatebasee",
|
||||
AmbiguousName: "",
|
||||
Domain: "example.com:8000",
|
||||
},
|
||||
{
|
||||
RemoteName: "library/ubuntu-12.04-base",
|
||||
FamiliarName: "ubuntu-12.04-base",
|
||||
FullName: "docker.io/library/ubuntu-12.04-base",
|
||||
AmbiguousName: "index.docker.io/library/ubuntu-12.04-base",
|
||||
Domain: "docker.io",
|
||||
},
|
||||
{
|
||||
RemoteName: "library/foo",
|
||||
FamiliarName: "foo",
|
||||
FullName: "docker.io/library/foo",
|
||||
AmbiguousName: "docker.io/foo",
|
||||
Domain: "docker.io",
|
||||
},
|
||||
{
|
||||
RemoteName: "library/foo/bar",
|
||||
FamiliarName: "library/foo/bar",
|
||||
FullName: "docker.io/library/foo/bar",
|
||||
AmbiguousName: "",
|
||||
Domain: "docker.io",
|
||||
},
|
||||
{
|
||||
RemoteName: "store/foo/bar",
|
||||
FamiliarName: "store/foo/bar",
|
||||
FullName: "docker.io/store/foo/bar",
|
||||
AmbiguousName: "",
|
||||
Domain: "docker.io",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tcase := range tcases {
|
||||
refStrings := []string{tcase.FamiliarName, tcase.FullName}
|
||||
if tcase.AmbiguousName != "" {
|
||||
refStrings = append(refStrings, tcase.AmbiguousName)
|
||||
}
|
||||
|
||||
var refs []Named
|
||||
for _, r := range refStrings {
|
||||
named, err := ParseNormalizedNamed(r)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
refs = append(refs, named)
|
||||
}
|
||||
|
||||
for _, r := range refs {
|
||||
if expected, actual := tcase.FamiliarName, FamiliarName(r); expected != actual {
|
||||
t.Fatalf("Invalid normalized reference for %q. Expected %q, got %q", r, expected, actual)
|
||||
}
|
||||
if expected, actual := tcase.FullName, r.String(); expected != actual {
|
||||
t.Fatalf("Invalid canonical reference for %q. Expected %q, got %q", r, expected, actual)
|
||||
}
|
||||
if expected, actual := tcase.Domain, Domain(r); expected != actual {
|
||||
t.Fatalf("Invalid domain for %q. Expected %q, got %q", r, expected, actual)
|
||||
}
|
||||
if expected, actual := tcase.RemoteName, Path(r); expected != actual {
|
||||
t.Fatalf("Invalid remoteName for %q. Expected %q, got %q", r, expected, actual)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseReferenceWithTagAndDigest(t *testing.T) {
|
||||
shortRef := "busybox:latest@sha256:86e0e091d0da6bde2456dbb48306f3956bbeb2eae1b5b9a43045843f69fe4aaa"
|
||||
ref, err := ParseNormalizedNamed(shortRef)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if expected, actual := "docker.io/library/"+shortRef, ref.String(); actual != expected {
|
||||
t.Fatalf("Invalid parsed reference for %q: expected %q, got %q", ref, expected, actual)
|
||||
}
|
||||
|
||||
if _, isTagged := ref.(NamedTagged); !isTagged {
|
||||
t.Fatalf("Reference from %q should support tag", ref)
|
||||
}
|
||||
if _, isCanonical := ref.(Canonical); !isCanonical {
|
||||
t.Fatalf("Reference from %q should support digest", ref)
|
||||
}
|
||||
if expected, actual := shortRef, FamiliarString(ref); actual != expected {
|
||||
t.Fatalf("Invalid parsed reference for %q: expected %q, got %q", ref, expected, actual)
|
||||
}
|
||||
}
|
||||
|
||||
func TestInvalidReferenceComponents(t *testing.T) {
|
||||
if _, err := ParseNormalizedNamed("-foo"); err == nil {
|
||||
t.Fatal("Expected WithName to detect invalid name")
|
||||
}
|
||||
ref, err := ParseNormalizedNamed("busybox")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := WithTag(ref, "-foo"); err == nil {
|
||||
t.Fatal("Expected WithName to detect invalid tag")
|
||||
}
|
||||
if _, err := WithDigest(ref, digest.Digest("foo")); err == nil {
|
||||
t.Fatal("Expected WithDigest to detect invalid digest")
|
||||
}
|
||||
}
|
||||
|
||||
func equalReference(r1, r2 Reference) bool {
|
||||
switch v1 := r1.(type) {
|
||||
case digestReference:
|
||||
if v2, ok := r2.(digestReference); ok {
|
||||
return v1 == v2
|
||||
}
|
||||
case repository:
|
||||
if v2, ok := r2.(repository); ok {
|
||||
return v1 == v2
|
||||
}
|
||||
case taggedReference:
|
||||
if v2, ok := r2.(taggedReference); ok {
|
||||
return v1 == v2
|
||||
}
|
||||
case canonicalReference:
|
||||
if v2, ok := r2.(canonicalReference); ok {
|
||||
return v1 == v2
|
||||
}
|
||||
case reference:
|
||||
if v2, ok := r2.(reference); ok {
|
||||
return v1 == v2
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func TestParseAnyReference(t *testing.T) {
|
||||
tcases := []struct {
|
||||
Reference string
|
||||
Equivalent string
|
||||
Expected Reference
|
||||
Digests []digest.Digest
|
||||
}{
|
||||
{
|
||||
Reference: "redis",
|
||||
Equivalent: "docker.io/library/redis",
|
||||
},
|
||||
{
|
||||
Reference: "redis:latest",
|
||||
Equivalent: "docker.io/library/redis:latest",
|
||||
},
|
||||
{
|
||||
Reference: "docker.io/library/redis:latest",
|
||||
Equivalent: "docker.io/library/redis:latest",
|
||||
},
|
||||
{
|
||||
Reference: "redis@sha256:dbcc1c35ac38df41fd2f5e4130b32ffdb93ebae8b3dbe638c23575912276fc9c",
|
||||
Equivalent: "docker.io/library/redis@sha256:dbcc1c35ac38df41fd2f5e4130b32ffdb93ebae8b3dbe638c23575912276fc9c",
|
||||
},
|
||||
{
|
||||
Reference: "docker.io/library/redis@sha256:dbcc1c35ac38df41fd2f5e4130b32ffdb93ebae8b3dbe638c23575912276fc9c",
|
||||
Equivalent: "docker.io/library/redis@sha256:dbcc1c35ac38df41fd2f5e4130b32ffdb93ebae8b3dbe638c23575912276fc9c",
|
||||
},
|
||||
{
|
||||
Reference: "dmcgowan/myapp",
|
||||
Equivalent: "docker.io/dmcgowan/myapp",
|
||||
},
|
||||
{
|
||||
Reference: "dmcgowan/myapp:latest",
|
||||
Equivalent: "docker.io/dmcgowan/myapp:latest",
|
||||
},
|
||||
{
|
||||
Reference: "docker.io/mcgowan/myapp:latest",
|
||||
Equivalent: "docker.io/mcgowan/myapp:latest",
|
||||
},
|
||||
{
|
||||
Reference: "dmcgowan/myapp@sha256:dbcc1c35ac38df41fd2f5e4130b32ffdb93ebae8b3dbe638c23575912276fc9c",
|
||||
Equivalent: "docker.io/dmcgowan/myapp@sha256:dbcc1c35ac38df41fd2f5e4130b32ffdb93ebae8b3dbe638c23575912276fc9c",
|
||||
},
|
||||
{
|
||||
Reference: "docker.io/dmcgowan/myapp@sha256:dbcc1c35ac38df41fd2f5e4130b32ffdb93ebae8b3dbe638c23575912276fc9c",
|
||||
Equivalent: "docker.io/dmcgowan/myapp@sha256:dbcc1c35ac38df41fd2f5e4130b32ffdb93ebae8b3dbe638c23575912276fc9c",
|
||||
},
|
||||
{
|
||||
Reference: "dbcc1c35ac38df41fd2f5e4130b32ffdb93ebae8b3dbe638c23575912276fc9c",
|
||||
Expected: digestReference("sha256:dbcc1c35ac38df41fd2f5e4130b32ffdb93ebae8b3dbe638c23575912276fc9c"),
|
||||
Equivalent: "sha256:dbcc1c35ac38df41fd2f5e4130b32ffdb93ebae8b3dbe638c23575912276fc9c",
|
||||
},
|
||||
{
|
||||
Reference: "sha256:dbcc1c35ac38df41fd2f5e4130b32ffdb93ebae8b3dbe638c23575912276fc9c",
|
||||
Expected: digestReference("sha256:dbcc1c35ac38df41fd2f5e4130b32ffdb93ebae8b3dbe638c23575912276fc9c"),
|
||||
Equivalent: "sha256:dbcc1c35ac38df41fd2f5e4130b32ffdb93ebae8b3dbe638c23575912276fc9c",
|
||||
},
|
||||
{
|
||||
Reference: "dbcc1c35ac38df41fd2f5e4130b32ffdb93ebae8b3dbe638c23575912276fc9",
|
||||
Equivalent: "docker.io/library/dbcc1c35ac38df41fd2f5e4130b32ffdb93ebae8b3dbe638c23575912276fc9",
|
||||
},
|
||||
{
|
||||
Reference: "dbcc1c35ac38df41fd2f5e4130b32ffdb93ebae8b3dbe638c23575912276fc9",
|
||||
Expected: digestReference("sha256:dbcc1c35ac38df41fd2f5e4130b32ffdb93ebae8b3dbe638c23575912276fc9c"),
|
||||
Equivalent: "sha256:dbcc1c35ac38df41fd2f5e4130b32ffdb93ebae8b3dbe638c23575912276fc9c",
|
||||
Digests: []digest.Digest{
|
||||
digest.Digest("sha256:dbcc1c35ac38df41fd2f5e4130b32ffdb93ebae8b3dbe638c23575912276fc9c"),
|
||||
digest.Digest("sha256:abcc1c35ac38df41fd2f5e4130b32ffdb93ebae8b3dbe638c23575912276fc9c"),
|
||||
},
|
||||
},
|
||||
{
|
||||
Reference: "dbcc1c35ac38df41fd2f5e4130b32ffdb93ebae8b3dbe638c23575912276fc9",
|
||||
Equivalent: "docker.io/library/dbcc1c35ac38df41fd2f5e4130b32ffdb93ebae8b3dbe638c23575912276fc9",
|
||||
Digests: []digest.Digest{
|
||||
digest.Digest("sha256:abcc1c35ac38df41fd2f5e4130b32ffdb93ebae8b3dbe638c23575912276fc9c"),
|
||||
},
|
||||
},
|
||||
{
|
||||
Reference: "dbcc1c",
|
||||
Expected: digestReference("sha256:dbcc1c35ac38df41fd2f5e4130b32ffdb93ebae8b3dbe638c23575912276fc9c"),
|
||||
Equivalent: "sha256:dbcc1c35ac38df41fd2f5e4130b32ffdb93ebae8b3dbe638c23575912276fc9c",
|
||||
Digests: []digest.Digest{
|
||||
digest.Digest("sha256:dbcc1c35ac38df41fd2f5e4130b32ffdb93ebae8b3dbe638c23575912276fc9c"),
|
||||
digest.Digest("sha256:abcc1c35ac38df41fd2f5e4130b32ffdb93ebae8b3dbe638c23575912276fc9c"),
|
||||
},
|
||||
},
|
||||
{
|
||||
Reference: "dbcc1",
|
||||
Equivalent: "docker.io/library/dbcc1",
|
||||
Digests: []digest.Digest{
|
||||
digest.Digest("sha256:dbcc1c35ac38df41fd2f5e4130b32ffdb93ebae8b3dbe638c23575912276fc9c"),
|
||||
digest.Digest("sha256:abcc1c35ac38df41fd2f5e4130b32ffdb93ebae8b3dbe638c23575912276fc9c"),
|
||||
},
|
||||
},
|
||||
{
|
||||
Reference: "dbcc1c",
|
||||
Equivalent: "docker.io/library/dbcc1c",
|
||||
Digests: []digest.Digest{
|
||||
digest.Digest("sha256:abcc1c35ac38df41fd2f5e4130b32ffdb93ebae8b3dbe638c23575912276fc9c"),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tcase := range tcases {
|
||||
var ref Reference
|
||||
var err error
|
||||
if len(tcase.Digests) == 0 {
|
||||
ref, err = ParseAnyReference(tcase.Reference)
|
||||
} else {
|
||||
ds := digestset.NewSet()
|
||||
for _, dgst := range tcase.Digests {
|
||||
if err := ds.Add(dgst); err != nil {
|
||||
t.Fatalf("Error adding digest %s: %v", dgst.String(), err)
|
||||
}
|
||||
}
|
||||
ref, err = ParseAnyReferenceWithSet(tcase.Reference, ds)
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatalf("Error parsing reference %s: %v", tcase.Reference, err)
|
||||
}
|
||||
if ref.String() != tcase.Equivalent {
|
||||
t.Fatalf("Unexpected string: %s, expected %s", ref.String(), tcase.Equivalent)
|
||||
}
|
||||
|
||||
expected := tcase.Expected
|
||||
if expected == nil {
|
||||
expected, err = Parse(tcase.Equivalent)
|
||||
if err != nil {
|
||||
t.Fatalf("Error parsing reference %s: %v", tcase.Equivalent, err)
|
||||
}
|
||||
}
|
||||
if !equalReference(ref, expected) {
|
||||
t.Errorf("Unexpected reference %#v, expected %#v", ref, expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestNormalizedSplitHostname(t *testing.T) {
|
||||
testcases := []struct {
|
||||
input string
|
||||
domain string
|
||||
name string
|
||||
}{
|
||||
{
|
||||
input: "test.com/foo",
|
||||
domain: "test.com",
|
||||
name: "foo",
|
||||
},
|
||||
{
|
||||
input: "test_com/foo",
|
||||
domain: "docker.io",
|
||||
name: "test_com/foo",
|
||||
},
|
||||
{
|
||||
input: "docker/migrator",
|
||||
domain: "docker.io",
|
||||
name: "docker/migrator",
|
||||
},
|
||||
{
|
||||
input: "test.com:8080/foo",
|
||||
domain: "test.com:8080",
|
||||
name: "foo",
|
||||
},
|
||||
{
|
||||
input: "test-com:8080/foo",
|
||||
domain: "test-com:8080",
|
||||
name: "foo",
|
||||
},
|
||||
{
|
||||
input: "foo",
|
||||
domain: "docker.io",
|
||||
name: "library/foo",
|
||||
},
|
||||
{
|
||||
input: "xn--n3h.com/foo",
|
||||
domain: "xn--n3h.com",
|
||||
name: "foo",
|
||||
},
|
||||
{
|
||||
input: "xn--n3h.com:18080/foo",
|
||||
domain: "xn--n3h.com:18080",
|
||||
name: "foo",
|
||||
},
|
||||
{
|
||||
input: "docker.io/foo",
|
||||
domain: "docker.io",
|
||||
name: "library/foo",
|
||||
},
|
||||
{
|
||||
input: "docker.io/library/foo",
|
||||
domain: "docker.io",
|
||||
name: "library/foo",
|
||||
},
|
||||
{
|
||||
input: "docker.io/library/foo/bar",
|
||||
domain: "docker.io",
|
||||
name: "library/foo/bar",
|
||||
},
|
||||
}
|
||||
for _, testcase := range testcases {
|
||||
failf := func(format string, v ...interface{}) {
|
||||
t.Logf(strconv.Quote(testcase.input)+": "+format, v...)
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
named, err := ParseNormalizedNamed(testcase.input)
|
||||
if err != nil {
|
||||
failf("error parsing name: %s", err)
|
||||
}
|
||||
domain, name := SplitHostname(named)
|
||||
if domain != testcase.domain {
|
||||
failf("unexpected domain: got %q, expected %q", domain, testcase.domain)
|
||||
}
|
||||
if name != testcase.name {
|
||||
failf("unexpected name: got %q, expected %q", name, testcase.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMatchError(t *testing.T) {
|
||||
named, err := ParseAnyReference("foo")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
_, err = FamiliarMatch("[-x]", named)
|
||||
if err == nil {
|
||||
t.Fatalf("expected an error, got nothing")
|
||||
}
|
||||
}
|
||||
|
||||
func TestMatch(t *testing.T) {
|
||||
matchCases := []struct {
|
||||
reference string
|
||||
pattern string
|
||||
expected bool
|
||||
}{
|
||||
{
|
||||
reference: "foo",
|
||||
pattern: "foo/**/ba[rz]",
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
reference: "foo/any/bat",
|
||||
pattern: "foo/**/ba[rz]",
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
reference: "foo/a/bar",
|
||||
pattern: "foo/**/ba[rz]",
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
reference: "foo/b/baz",
|
||||
pattern: "foo/**/ba[rz]",
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
reference: "foo/c/baz:tag",
|
||||
pattern: "foo/**/ba[rz]",
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
reference: "foo/c/baz:tag",
|
||||
pattern: "foo/*/baz:tag",
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
reference: "foo/c/baz:tag",
|
||||
pattern: "foo/c/baz:tag",
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
reference: "example.com/foo/c/baz:tag",
|
||||
pattern: "*/foo/c/baz",
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
reference: "example.com/foo/c/baz:tag",
|
||||
pattern: "example.com/foo/c/baz",
|
||||
expected: true,
|
||||
},
|
||||
}
|
||||
for _, c := range matchCases {
|
||||
named, err := ParseAnyReference(c.reference)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
actual, err := FamiliarMatch(c.pattern, named)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if actual != c.expected {
|
||||
t.Fatalf("expected %s match %s to be %v, was %v", c.reference, c.pattern, c.expected, actual)
|
||||
}
|
||||
}
|
||||
}
|
||||
211
vendor/github.com/docker/distribution/reference/reference.go
generated
vendored
211
vendor/github.com/docker/distribution/reference/reference.go
generated
vendored
@@ -4,30 +4,32 @@
|
||||
// Grammar
|
||||
//
|
||||
// reference := name [ ":" tag ] [ "@" digest ]
|
||||
// name := [hostname '/'] component ['/' component]*
|
||||
// hostname := hostcomponent ['.' hostcomponent]* [':' port-number]
|
||||
// hostcomponent := /([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9])/
|
||||
// name := [domain '/'] path-component ['/' path-component]*
|
||||
// domain := domain-component ['.' domain-component]* [':' port-number]
|
||||
// domain-component := /([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9])/
|
||||
// port-number := /[0-9]+/
|
||||
// component := alpha-numeric [separator alpha-numeric]*
|
||||
// path-component := alpha-numeric [separator alpha-numeric]*
|
||||
// alpha-numeric := /[a-z0-9]+/
|
||||
// separator := /[_.]|__|[-]*/
|
||||
//
|
||||
// tag := /[\w][\w.-]{0,127}/
|
||||
//
|
||||
// digest := digest-algorithm ":" digest-hex
|
||||
// digest-algorithm := digest-algorithm-component [ digest-algorithm-separator digest-algorithm-component ]
|
||||
// digest-algorithm := digest-algorithm-component [ digest-algorithm-separator digest-algorithm-component ]*
|
||||
// digest-algorithm-separator := /[+.-_]/
|
||||
// digest-algorithm-component := /[A-Za-z][A-Za-z0-9]*/
|
||||
// digest-hex := /[0-9a-fA-F]{32,}/ ; At least 128 bit digest value
|
||||
//
|
||||
// identifier := /[a-f0-9]{64}/
|
||||
// short-identifier := /[a-f0-9]{6,64}/
|
||||
package reference
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/distribution/digest"
|
||||
"github.com/opencontainers/go-digest"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -53,6 +55,9 @@ var (
|
||||
|
||||
// ErrNameTooLong is returned when a repository name is longer than NameTotalLengthMax.
|
||||
ErrNameTooLong = fmt.Errorf("repository name must not be more than %v characters", NameTotalLengthMax)
|
||||
|
||||
// ErrNameNotCanonical is returned when a name is not canonical.
|
||||
ErrNameNotCanonical = errors.New("repository name must be canonical")
|
||||
)
|
||||
|
||||
// Reference is an opaque object reference identifier that may include
|
||||
@@ -126,23 +131,56 @@ type Digested interface {
|
||||
}
|
||||
|
||||
// Canonical reference is an object with a fully unique
|
||||
// name including a name with hostname and digest
|
||||
// name including a name with domain and digest
|
||||
type Canonical interface {
|
||||
Named
|
||||
Digest() digest.Digest
|
||||
}
|
||||
|
||||
// namedRepository is a reference to a repository with a name.
|
||||
// A namedRepository has both domain and path components.
|
||||
type namedRepository interface {
|
||||
Named
|
||||
Domain() string
|
||||
Path() string
|
||||
}
|
||||
|
||||
// Domain returns the domain part of the Named reference
|
||||
func Domain(named Named) string {
|
||||
if r, ok := named.(namedRepository); ok {
|
||||
return r.Domain()
|
||||
}
|
||||
domain, _ := splitDomain(named.Name())
|
||||
return domain
|
||||
}
|
||||
|
||||
// Path returns the name without the domain part of the Named reference
|
||||
func Path(named Named) (name string) {
|
||||
if r, ok := named.(namedRepository); ok {
|
||||
return r.Path()
|
||||
}
|
||||
_, path := splitDomain(named.Name())
|
||||
return path
|
||||
}
|
||||
|
||||
func splitDomain(name string) (string, string) {
|
||||
match := anchoredNameRegexp.FindStringSubmatch(name)
|
||||
if len(match) != 3 {
|
||||
return "", name
|
||||
}
|
||||
return match[1], match[2]
|
||||
}
|
||||
|
||||
// SplitHostname splits a named reference into a
|
||||
// hostname and name string. If no valid hostname is
|
||||
// found, the hostname is empty and the full value
|
||||
// is returned as name
|
||||
// DEPRECATED: Use Domain or Path
|
||||
func SplitHostname(named Named) (string, string) {
|
||||
name := named.Name()
|
||||
match := anchoredNameRegexp.FindStringSubmatch(name)
|
||||
if len(match) != 3 {
|
||||
return "", name
|
||||
if r, ok := named.(namedRepository); ok {
|
||||
return r.Domain(), r.Path()
|
||||
}
|
||||
return match[1], match[2]
|
||||
return splitDomain(named.Name())
|
||||
}
|
||||
|
||||
// Parse parses s and returns a syntactically valid Reference.
|
||||
@@ -164,13 +202,24 @@ func Parse(s string) (Reference, error) {
|
||||
return nil, ErrNameTooLong
|
||||
}
|
||||
|
||||
var repo repository
|
||||
|
||||
nameMatch := anchoredNameRegexp.FindStringSubmatch(matches[1])
|
||||
if nameMatch != nil && len(nameMatch) == 3 {
|
||||
repo.domain = nameMatch[1]
|
||||
repo.path = nameMatch[2]
|
||||
} else {
|
||||
repo.domain = ""
|
||||
repo.path = matches[1]
|
||||
}
|
||||
|
||||
ref := reference{
|
||||
name: matches[1],
|
||||
tag: matches[2],
|
||||
namedRepository: repo,
|
||||
tag: matches[2],
|
||||
}
|
||||
if matches[3] != "" {
|
||||
var err error
|
||||
ref.digest, err = digest.ParseDigest(matches[3])
|
||||
ref.digest, err = digest.Parse(matches[3])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -185,18 +234,17 @@ func Parse(s string) (Reference, error) {
|
||||
}
|
||||
|
||||
// ParseNamed parses s and returns a syntactically valid reference implementing
|
||||
// the Named interface. The reference must have a name, otherwise an error is
|
||||
// returned.
|
||||
// the Named interface. The reference must have a name and be in the canonical
|
||||
// form, otherwise an error is returned.
|
||||
// If an error was encountered it is returned, along with a nil Reference.
|
||||
// NOTE: ParseNamed will not handle short digests.
|
||||
func ParseNamed(s string) (Named, error) {
|
||||
ref, err := Parse(s)
|
||||
named, err := ParseNormalizedNamed(s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
named, isNamed := ref.(Named)
|
||||
if !isNamed {
|
||||
return nil, fmt.Errorf("reference %s has no name", ref.String())
|
||||
if named.String() != s {
|
||||
return nil, ErrNameNotCanonical
|
||||
}
|
||||
return named, nil
|
||||
}
|
||||
@@ -207,10 +255,15 @@ func WithName(name string) (Named, error) {
|
||||
if len(name) > NameTotalLengthMax {
|
||||
return nil, ErrNameTooLong
|
||||
}
|
||||
if !anchoredNameRegexp.MatchString(name) {
|
||||
|
||||
match := anchoredNameRegexp.FindStringSubmatch(name)
|
||||
if match == nil || len(match) != 3 {
|
||||
return nil, ErrReferenceInvalidFormat
|
||||
}
|
||||
return repository(name), nil
|
||||
return repository{
|
||||
domain: match[1],
|
||||
path: match[2],
|
||||
}, nil
|
||||
}
|
||||
|
||||
// WithTag combines the name from "name" and the tag from "tag" to form a
|
||||
@@ -219,16 +272,23 @@ func WithTag(name Named, tag string) (NamedTagged, error) {
|
||||
if !anchoredTagRegexp.MatchString(tag) {
|
||||
return nil, ErrTagInvalidFormat
|
||||
}
|
||||
var repo repository
|
||||
if r, ok := name.(namedRepository); ok {
|
||||
repo.domain = r.Domain()
|
||||
repo.path = r.Path()
|
||||
} else {
|
||||
repo.path = name.Name()
|
||||
}
|
||||
if canonical, ok := name.(Canonical); ok {
|
||||
return reference{
|
||||
name: name.Name(),
|
||||
tag: tag,
|
||||
digest: canonical.Digest(),
|
||||
namedRepository: repo,
|
||||
tag: tag,
|
||||
digest: canonical.Digest(),
|
||||
}, nil
|
||||
}
|
||||
return taggedReference{
|
||||
name: name.Name(),
|
||||
tag: tag,
|
||||
namedRepository: repo,
|
||||
tag: tag,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -238,36 +298,37 @@ func WithDigest(name Named, digest digest.Digest) (Canonical, error) {
|
||||
if !anchoredDigestRegexp.MatchString(digest.String()) {
|
||||
return nil, ErrDigestInvalidFormat
|
||||
}
|
||||
var repo repository
|
||||
if r, ok := name.(namedRepository); ok {
|
||||
repo.domain = r.Domain()
|
||||
repo.path = r.Path()
|
||||
} else {
|
||||
repo.path = name.Name()
|
||||
}
|
||||
if tagged, ok := name.(Tagged); ok {
|
||||
return reference{
|
||||
name: name.Name(),
|
||||
tag: tagged.Tag(),
|
||||
digest: digest,
|
||||
namedRepository: repo,
|
||||
tag: tagged.Tag(),
|
||||
digest: digest,
|
||||
}, nil
|
||||
}
|
||||
return canonicalReference{
|
||||
name: name.Name(),
|
||||
digest: digest,
|
||||
namedRepository: repo,
|
||||
digest: digest,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Match reports whether ref matches the specified pattern.
|
||||
// See https://godoc.org/path#Match for supported patterns.
|
||||
func Match(pattern string, ref Reference) (bool, error) {
|
||||
matched, err := path.Match(pattern, ref.String())
|
||||
if namedRef, isNamed := ref.(Named); isNamed && !matched {
|
||||
matched, _ = path.Match(pattern, namedRef.Name())
|
||||
}
|
||||
return matched, err
|
||||
}
|
||||
|
||||
// TrimNamed removes any tag or digest from the named reference.
|
||||
func TrimNamed(ref Named) Named {
|
||||
return repository(ref.Name())
|
||||
domain, path := SplitHostname(ref)
|
||||
return repository{
|
||||
domain: domain,
|
||||
path: path,
|
||||
}
|
||||
}
|
||||
|
||||
func getBestReferenceType(ref reference) Reference {
|
||||
if ref.name == "" {
|
||||
if ref.Name() == "" {
|
||||
// Allow digest only references
|
||||
if ref.digest != "" {
|
||||
return digestReference(ref.digest)
|
||||
@@ -277,16 +338,16 @@ func getBestReferenceType(ref reference) Reference {
|
||||
if ref.tag == "" {
|
||||
if ref.digest != "" {
|
||||
return canonicalReference{
|
||||
name: ref.name,
|
||||
digest: ref.digest,
|
||||
namedRepository: ref.namedRepository,
|
||||
digest: ref.digest,
|
||||
}
|
||||
}
|
||||
return repository(ref.name)
|
||||
return ref.namedRepository
|
||||
}
|
||||
if ref.digest == "" {
|
||||
return taggedReference{
|
||||
name: ref.name,
|
||||
tag: ref.tag,
|
||||
namedRepository: ref.namedRepository,
|
||||
tag: ref.tag,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -294,17 +355,13 @@ func getBestReferenceType(ref reference) Reference {
|
||||
}
|
||||
|
||||
type reference struct {
|
||||
name string
|
||||
namedRepository
|
||||
tag string
|
||||
digest digest.Digest
|
||||
}
|
||||
|
||||
func (r reference) String() string {
|
||||
return r.name + ":" + r.tag + "@" + r.digest.String()
|
||||
}
|
||||
|
||||
func (r reference) Name() string {
|
||||
return r.name
|
||||
return r.Name() + ":" + r.tag + "@" + r.digest.String()
|
||||
}
|
||||
|
||||
func (r reference) Tag() string {
|
||||
@@ -315,20 +372,34 @@ func (r reference) Digest() digest.Digest {
|
||||
return r.digest
|
||||
}
|
||||
|
||||
type repository string
|
||||
type repository struct {
|
||||
domain string
|
||||
path string
|
||||
}
|
||||
|
||||
func (r repository) String() string {
|
||||
return string(r)
|
||||
return r.Name()
|
||||
}
|
||||
|
||||
func (r repository) Name() string {
|
||||
return string(r)
|
||||
if r.domain == "" {
|
||||
return r.path
|
||||
}
|
||||
return r.domain + "/" + r.path
|
||||
}
|
||||
|
||||
func (r repository) Domain() string {
|
||||
return r.domain
|
||||
}
|
||||
|
||||
func (r repository) Path() string {
|
||||
return r.path
|
||||
}
|
||||
|
||||
type digestReference digest.Digest
|
||||
|
||||
func (d digestReference) String() string {
|
||||
return d.String()
|
||||
return digest.Digest(d).String()
|
||||
}
|
||||
|
||||
func (d digestReference) Digest() digest.Digest {
|
||||
@@ -336,16 +407,12 @@ func (d digestReference) Digest() digest.Digest {
|
||||
}
|
||||
|
||||
type taggedReference struct {
|
||||
name string
|
||||
tag string
|
||||
namedRepository
|
||||
tag string
|
||||
}
|
||||
|
||||
func (t taggedReference) String() string {
|
||||
return t.name + ":" + t.tag
|
||||
}
|
||||
|
||||
func (t taggedReference) Name() string {
|
||||
return t.name
|
||||
return t.Name() + ":" + t.tag
|
||||
}
|
||||
|
||||
func (t taggedReference) Tag() string {
|
||||
@@ -353,16 +420,12 @@ func (t taggedReference) Tag() string {
|
||||
}
|
||||
|
||||
type canonicalReference struct {
|
||||
name string
|
||||
namedRepository
|
||||
digest digest.Digest
|
||||
}
|
||||
|
||||
func (c canonicalReference) String() string {
|
||||
return c.name + "@" + c.digest.String()
|
||||
}
|
||||
|
||||
func (c canonicalReference) Name() string {
|
||||
return c.name
|
||||
return c.Name() + "@" + c.digest.String()
|
||||
}
|
||||
|
||||
func (c canonicalReference) Digest() digest.Digest {
|
||||
|
||||
194
vendor/github.com/docker/distribution/reference/reference_test.go
generated
vendored
194
vendor/github.com/docker/distribution/reference/reference_test.go
generated
vendored
@@ -1,12 +1,14 @@
|
||||
package reference
|
||||
|
||||
import (
|
||||
_ "crypto/sha256"
|
||||
_ "crypto/sha512"
|
||||
"encoding/json"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/distribution/digest"
|
||||
"github.com/opencontainers/go-digest"
|
||||
)
|
||||
|
||||
func TestReferenceParse(t *testing.T) {
|
||||
@@ -19,8 +21,8 @@ func TestReferenceParse(t *testing.T) {
|
||||
err error
|
||||
// repository is the string representation for the reference
|
||||
repository string
|
||||
// hostname is the hostname expected in the reference
|
||||
hostname string
|
||||
// domain is the domain expected in the reference
|
||||
domain string
|
||||
// tag is the tag for the reference
|
||||
tag string
|
||||
// digest is the digest for the reference (enforces digest reference)
|
||||
@@ -42,37 +44,37 @@ func TestReferenceParse(t *testing.T) {
|
||||
},
|
||||
{
|
||||
input: "test.com/repo:tag",
|
||||
hostname: "test.com",
|
||||
domain: "test.com",
|
||||
repository: "test.com/repo",
|
||||
tag: "tag",
|
||||
},
|
||||
{
|
||||
input: "test:5000/repo",
|
||||
hostname: "test:5000",
|
||||
domain: "test:5000",
|
||||
repository: "test:5000/repo",
|
||||
},
|
||||
{
|
||||
input: "test:5000/repo:tag",
|
||||
hostname: "test:5000",
|
||||
domain: "test:5000",
|
||||
repository: "test:5000/repo",
|
||||
tag: "tag",
|
||||
},
|
||||
{
|
||||
input: "test:5000/repo@sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||
hostname: "test:5000",
|
||||
domain: "test:5000",
|
||||
repository: "test:5000/repo",
|
||||
digest: "sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||
},
|
||||
{
|
||||
input: "test:5000/repo:tag@sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||
hostname: "test:5000",
|
||||
domain: "test:5000",
|
||||
repository: "test:5000/repo",
|
||||
tag: "tag",
|
||||
digest: "sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||
},
|
||||
{
|
||||
input: "test:5000/repo",
|
||||
hostname: "test:5000",
|
||||
domain: "test:5000",
|
||||
repository: "test:5000/repo",
|
||||
},
|
||||
{
|
||||
@@ -120,7 +122,7 @@ func TestReferenceParse(t *testing.T) {
|
||||
},
|
||||
{
|
||||
input: strings.Repeat("a/", 127) + "a:tag-puts-this-over-max",
|
||||
hostname: "a",
|
||||
domain: "a",
|
||||
repository: strings.Repeat("a/", 127) + "a",
|
||||
tag: "tag-puts-this-over-max",
|
||||
},
|
||||
@@ -130,30 +132,30 @@ func TestReferenceParse(t *testing.T) {
|
||||
},
|
||||
{
|
||||
input: "sub-dom1.foo.com/bar/baz/quux",
|
||||
hostname: "sub-dom1.foo.com",
|
||||
domain: "sub-dom1.foo.com",
|
||||
repository: "sub-dom1.foo.com/bar/baz/quux",
|
||||
},
|
||||
{
|
||||
input: "sub-dom1.foo.com/bar/baz/quux:some-long-tag",
|
||||
hostname: "sub-dom1.foo.com",
|
||||
domain: "sub-dom1.foo.com",
|
||||
repository: "sub-dom1.foo.com/bar/baz/quux",
|
||||
tag: "some-long-tag",
|
||||
},
|
||||
{
|
||||
input: "b.gcr.io/test.example.com/my-app:test.example.com",
|
||||
hostname: "b.gcr.io",
|
||||
domain: "b.gcr.io",
|
||||
repository: "b.gcr.io/test.example.com/my-app",
|
||||
tag: "test.example.com",
|
||||
},
|
||||
{
|
||||
input: "xn--n3h.com/myimage:xn--n3h.com", // ☃.com in punycode
|
||||
hostname: "xn--n3h.com",
|
||||
domain: "xn--n3h.com",
|
||||
repository: "xn--n3h.com/myimage",
|
||||
tag: "xn--n3h.com",
|
||||
},
|
||||
{
|
||||
input: "xn--7o8h.com/myimage:xn--7o8h.com@sha512:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", // 🐳.com in punycode
|
||||
hostname: "xn--7o8h.com",
|
||||
domain: "xn--7o8h.com",
|
||||
repository: "xn--7o8h.com/myimage",
|
||||
tag: "xn--7o8h.com",
|
||||
digest: "sha512:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||
@@ -165,7 +167,7 @@ func TestReferenceParse(t *testing.T) {
|
||||
},
|
||||
{
|
||||
input: "foo/foo_bar.com:8080",
|
||||
hostname: "foo",
|
||||
domain: "foo",
|
||||
repository: "foo/foo_bar.com",
|
||||
tag: "8080",
|
||||
},
|
||||
@@ -196,11 +198,11 @@ func TestReferenceParse(t *testing.T) {
|
||||
if named.Name() != testcase.repository {
|
||||
failf("unexpected repository: got %q, expected %q", named.Name(), testcase.repository)
|
||||
}
|
||||
hostname, _ := SplitHostname(named)
|
||||
if hostname != testcase.hostname {
|
||||
failf("unexpected hostname: got %q, expected %q", hostname, testcase.hostname)
|
||||
domain, _ := SplitHostname(named)
|
||||
if domain != testcase.domain {
|
||||
failf("unexpected domain: got %q, expected %q", domain, testcase.domain)
|
||||
}
|
||||
} else if testcase.repository != "" || testcase.hostname != "" {
|
||||
} else if testcase.repository != "" || testcase.domain != "" {
|
||||
failf("expected named type, got %T", repo)
|
||||
}
|
||||
|
||||
@@ -280,39 +282,39 @@ func TestWithNameFailure(t *testing.T) {
|
||||
|
||||
func TestSplitHostname(t *testing.T) {
|
||||
testcases := []struct {
|
||||
input string
|
||||
hostname string
|
||||
name string
|
||||
input string
|
||||
domain string
|
||||
name string
|
||||
}{
|
||||
{
|
||||
input: "test.com/foo",
|
||||
hostname: "test.com",
|
||||
name: "foo",
|
||||
input: "test.com/foo",
|
||||
domain: "test.com",
|
||||
name: "foo",
|
||||
},
|
||||
{
|
||||
input: "test_com/foo",
|
||||
hostname: "",
|
||||
name: "test_com/foo",
|
||||
input: "test_com/foo",
|
||||
domain: "",
|
||||
name: "test_com/foo",
|
||||
},
|
||||
{
|
||||
input: "test:8080/foo",
|
||||
hostname: "test:8080",
|
||||
name: "foo",
|
||||
input: "test:8080/foo",
|
||||
domain: "test:8080",
|
||||
name: "foo",
|
||||
},
|
||||
{
|
||||
input: "test.com:8080/foo",
|
||||
hostname: "test.com:8080",
|
||||
name: "foo",
|
||||
input: "test.com:8080/foo",
|
||||
domain: "test.com:8080",
|
||||
name: "foo",
|
||||
},
|
||||
{
|
||||
input: "test-com:8080/foo",
|
||||
hostname: "test-com:8080",
|
||||
name: "foo",
|
||||
input: "test-com:8080/foo",
|
||||
domain: "test-com:8080",
|
||||
name: "foo",
|
||||
},
|
||||
{
|
||||
input: "xn--n3h.com:18080/foo",
|
||||
hostname: "xn--n3h.com:18080",
|
||||
name: "foo",
|
||||
input: "xn--n3h.com:18080/foo",
|
||||
domain: "xn--n3h.com:18080",
|
||||
name: "foo",
|
||||
},
|
||||
}
|
||||
for _, testcase := range testcases {
|
||||
@@ -325,9 +327,9 @@ func TestSplitHostname(t *testing.T) {
|
||||
if err != nil {
|
||||
failf("error parsing name: %s", err)
|
||||
}
|
||||
hostname, name := SplitHostname(named)
|
||||
if hostname != testcase.hostname {
|
||||
failf("unexpected hostname: got %q, expected %q", hostname, testcase.hostname)
|
||||
domain, name := SplitHostname(named)
|
||||
if domain != testcase.domain {
|
||||
failf("unexpected domain: got %q, expected %q", domain, testcase.domain)
|
||||
}
|
||||
if name != testcase.name {
|
||||
failf("unexpected name: got %q, expected %q", name, testcase.name)
|
||||
@@ -582,80 +584,76 @@ func TestWithDigest(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestMatchError(t *testing.T) {
|
||||
named, err := Parse("foo")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
_, err = Match("[-x]", named)
|
||||
if err == nil {
|
||||
t.Fatalf("expected an error, got nothing")
|
||||
}
|
||||
}
|
||||
|
||||
func TestMatch(t *testing.T) {
|
||||
matchCases := []struct {
|
||||
reference string
|
||||
pattern string
|
||||
expected bool
|
||||
func TestParseNamed(t *testing.T) {
|
||||
testcases := []struct {
|
||||
input string
|
||||
domain string
|
||||
name string
|
||||
err error
|
||||
}{
|
||||
{
|
||||
reference: "foo",
|
||||
pattern: "foo/**/ba[rz]",
|
||||
expected: false,
|
||||
input: "test.com/foo",
|
||||
domain: "test.com",
|
||||
name: "foo",
|
||||
},
|
||||
{
|
||||
reference: "foo/any/bat",
|
||||
pattern: "foo/**/ba[rz]",
|
||||
expected: false,
|
||||
input: "test:8080/foo",
|
||||
domain: "test:8080",
|
||||
name: "foo",
|
||||
},
|
||||
{
|
||||
reference: "foo/a/bar",
|
||||
pattern: "foo/**/ba[rz]",
|
||||
expected: true,
|
||||
input: "test_com/foo",
|
||||
err: ErrNameNotCanonical,
|
||||
},
|
||||
{
|
||||
reference: "foo/b/baz",
|
||||
pattern: "foo/**/ba[rz]",
|
||||
expected: true,
|
||||
input: "test.com",
|
||||
err: ErrNameNotCanonical,
|
||||
},
|
||||
{
|
||||
reference: "foo/c/baz:tag",
|
||||
pattern: "foo/**/ba[rz]",
|
||||
expected: true,
|
||||
input: "foo",
|
||||
err: ErrNameNotCanonical,
|
||||
},
|
||||
{
|
||||
reference: "foo/c/baz:tag",
|
||||
pattern: "foo/*/baz:tag",
|
||||
expected: true,
|
||||
input: "library/foo",
|
||||
err: ErrNameNotCanonical,
|
||||
},
|
||||
{
|
||||
reference: "foo/c/baz:tag",
|
||||
pattern: "foo/c/baz:tag",
|
||||
expected: true,
|
||||
input: "docker.io/library/foo",
|
||||
domain: "docker.io",
|
||||
name: "library/foo",
|
||||
},
|
||||
// Ambiguous case, parser will add "library/" to foo
|
||||
{
|
||||
reference: "example.com/foo/c/baz:tag",
|
||||
pattern: "*/foo/c/baz",
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
reference: "example.com/foo/c/baz:tag",
|
||||
pattern: "example.com/foo/c/baz",
|
||||
expected: true,
|
||||
input: "docker.io/foo",
|
||||
err: ErrNameNotCanonical,
|
||||
},
|
||||
}
|
||||
for _, c := range matchCases {
|
||||
named, err := Parse(c.reference)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
for _, testcase := range testcases {
|
||||
failf := func(format string, v ...interface{}) {
|
||||
t.Logf(strconv.Quote(testcase.input)+": "+format, v...)
|
||||
t.Fail()
|
||||
}
|
||||
actual, err := Match(c.pattern, named)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
|
||||
named, err := ParseNamed(testcase.input)
|
||||
if err != nil && testcase.err == nil {
|
||||
failf("error parsing name: %s", err)
|
||||
continue
|
||||
} else if err == nil && testcase.err != nil {
|
||||
failf("parsing succeded: expected error %v", testcase.err)
|
||||
continue
|
||||
} else if err != testcase.err {
|
||||
failf("unexpected error %v, expected %v", err, testcase.err)
|
||||
continue
|
||||
} else if err != nil {
|
||||
continue
|
||||
}
|
||||
if actual != c.expected {
|
||||
t.Fatalf("expected %s match %s to be %v, was %v", c.reference, c.pattern, c.expected, actual)
|
||||
|
||||
domain, name := SplitHostname(named)
|
||||
if domain != testcase.domain {
|
||||
failf("unexpected domain: got %q, expected %q", domain, testcase.domain)
|
||||
}
|
||||
if name != testcase.name {
|
||||
failf("unexpected name: got %q, expected %q", name, testcase.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
41
vendor/github.com/docker/distribution/reference/regexp.go
generated
vendored
41
vendor/github.com/docker/distribution/reference/regexp.go
generated
vendored
@@ -19,18 +19,18 @@ var (
|
||||
alphaNumericRegexp,
|
||||
optional(repeated(separatorRegexp, alphaNumericRegexp)))
|
||||
|
||||
// hostnameComponentRegexp restricts the registry hostname component of a
|
||||
// repository name to start with a component as defined by hostnameRegexp
|
||||
// domainComponentRegexp restricts the registry domain component of a
|
||||
// repository name to start with a component as defined by DomainRegexp
|
||||
// and followed by an optional port.
|
||||
hostnameComponentRegexp = match(`(?:[a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9])`)
|
||||
domainComponentRegexp = match(`(?:[a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9])`)
|
||||
|
||||
// hostnameRegexp defines the structure of potential hostname components
|
||||
// DomainRegexp defines the structure of potential domain components
|
||||
// that may be part of image names. This is purposely a subset of what is
|
||||
// allowed by DNS to ensure backwards compatibility with Docker image
|
||||
// names.
|
||||
hostnameRegexp = expression(
|
||||
hostnameComponentRegexp,
|
||||
optional(repeated(literal(`.`), hostnameComponentRegexp)),
|
||||
DomainRegexp = expression(
|
||||
domainComponentRegexp,
|
||||
optional(repeated(literal(`.`), domainComponentRegexp)),
|
||||
optional(literal(`:`), match(`[0-9]+`)))
|
||||
|
||||
// TagRegexp matches valid tag names. From docker/docker:graph/tags.go.
|
||||
@@ -48,17 +48,17 @@ var (
|
||||
anchoredDigestRegexp = anchored(DigestRegexp)
|
||||
|
||||
// NameRegexp is the format for the name component of references. The
|
||||
// regexp has capturing groups for the hostname and name part omitting
|
||||
// regexp has capturing groups for the domain and name part omitting
|
||||
// the separating forward slash from either.
|
||||
NameRegexp = expression(
|
||||
optional(hostnameRegexp, literal(`/`)),
|
||||
optional(DomainRegexp, literal(`/`)),
|
||||
nameComponentRegexp,
|
||||
optional(repeated(literal(`/`), nameComponentRegexp)))
|
||||
|
||||
// anchoredNameRegexp is used to parse a name value, capturing the
|
||||
// hostname and trailing components.
|
||||
// domain and trailing components.
|
||||
anchoredNameRegexp = anchored(
|
||||
optional(capture(hostnameRegexp), literal(`/`)),
|
||||
optional(capture(DomainRegexp), literal(`/`)),
|
||||
capture(nameComponentRegexp,
|
||||
optional(repeated(literal(`/`), nameComponentRegexp))))
|
||||
|
||||
@@ -68,6 +68,25 @@ var (
|
||||
ReferenceRegexp = anchored(capture(NameRegexp),
|
||||
optional(literal(":"), capture(TagRegexp)),
|
||||
optional(literal("@"), capture(DigestRegexp)))
|
||||
|
||||
// IdentifierRegexp is the format for string identifier used as a
|
||||
// content addressable identifier using sha256. These identifiers
|
||||
// are like digests without the algorithm, since sha256 is used.
|
||||
IdentifierRegexp = match(`([a-f0-9]{64})`)
|
||||
|
||||
// ShortIdentifierRegexp is the format used to represent a prefix
|
||||
// of an identifier. A prefix may be used to match a sha256 identifier
|
||||
// within a list of trusted identifiers.
|
||||
ShortIdentifierRegexp = match(`([a-f0-9]{6,64})`)
|
||||
|
||||
// anchoredIdentifierRegexp is used to check or match an
|
||||
// identifier value, anchored at start and end of string.
|
||||
anchoredIdentifierRegexp = anchored(IdentifierRegexp)
|
||||
|
||||
// anchoredShortIdentifierRegexp is used to check if a value
|
||||
// is a possible identifier prefix, anchored at start and end
|
||||
// of string.
|
||||
anchoredShortIdentifierRegexp = anchored(ShortIdentifierRegexp)
|
||||
)
|
||||
|
||||
// match compiles the string to a regular expression.
|
||||
|
||||
68
vendor/github.com/docker/distribution/reference/regexp_test.go
generated
vendored
68
vendor/github.com/docker/distribution/reference/regexp_test.go
generated
vendored
@@ -33,7 +33,7 @@ func checkRegexp(t *testing.T, r *regexp.Regexp, m regexpMatch) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestHostRegexp(t *testing.T) {
|
||||
func TestDomainRegexp(t *testing.T) {
|
||||
hostcases := []regexpMatch{
|
||||
{
|
||||
input: "test.com",
|
||||
@@ -116,7 +116,7 @@ func TestHostRegexp(t *testing.T) {
|
||||
match: true,
|
||||
},
|
||||
}
|
||||
r := regexp.MustCompile(`^` + hostnameRegexp.String() + `$`)
|
||||
r := regexp.MustCompile(`^` + DomainRegexp.String() + `$`)
|
||||
for i := range hostcases {
|
||||
checkRegexp(t, r, hostcases[i])
|
||||
}
|
||||
@@ -487,3 +487,67 @@ func TestReferenceRegexp(t *testing.T) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestIdentifierRegexp(t *testing.T) {
|
||||
fullCases := []regexpMatch{
|
||||
{
|
||||
input: "da304e823d8ca2b9d863a3c897baeb852ba21ea9a9f1414736394ae7fcaf9821",
|
||||
match: true,
|
||||
},
|
||||
{
|
||||
input: "7EC43B381E5AEFE6E04EFB0B3F0693FF2A4A50652D64AEC573905F2DB5889A1C",
|
||||
match: false,
|
||||
},
|
||||
{
|
||||
input: "da304e823d8ca2b9d863a3c897baeb852ba21ea9a9f1414736394ae7fcaf",
|
||||
match: false,
|
||||
},
|
||||
{
|
||||
input: "sha256:da304e823d8ca2b9d863a3c897baeb852ba21ea9a9f1414736394ae7fcaf9821",
|
||||
match: false,
|
||||
},
|
||||
{
|
||||
input: "da304e823d8ca2b9d863a3c897baeb852ba21ea9a9f1414736394ae7fcaf98218482",
|
||||
match: false,
|
||||
},
|
||||
}
|
||||
|
||||
shortCases := []regexpMatch{
|
||||
{
|
||||
input: "da304e823d8ca2b9d863a3c897baeb852ba21ea9a9f1414736394ae7fcaf9821",
|
||||
match: true,
|
||||
},
|
||||
{
|
||||
input: "7EC43B381E5AEFE6E04EFB0B3F0693FF2A4A50652D64AEC573905F2DB5889A1C",
|
||||
match: false,
|
||||
},
|
||||
{
|
||||
input: "da304e823d8ca2b9d863a3c897baeb852ba21ea9a9f1414736394ae7fcaf",
|
||||
match: true,
|
||||
},
|
||||
{
|
||||
input: "sha256:da304e823d8ca2b9d863a3c897baeb852ba21ea9a9f1414736394ae7fcaf9821",
|
||||
match: false,
|
||||
},
|
||||
{
|
||||
input: "da304e823d8ca2b9d863a3c897baeb852ba21ea9a9f1414736394ae7fcaf98218482",
|
||||
match: false,
|
||||
},
|
||||
{
|
||||
input: "da304",
|
||||
match: false,
|
||||
},
|
||||
{
|
||||
input: "da304e",
|
||||
match: true,
|
||||
},
|
||||
}
|
||||
|
||||
for i := range fullCases {
|
||||
checkRegexp(t, anchoredIdentifierRegexp, fullCases[i])
|
||||
}
|
||||
|
||||
for i := range shortCases {
|
||||
checkRegexp(t, anchoredShortIdentifierRegexp, shortCases[i])
|
||||
}
|
||||
}
|
||||
|
||||
2
vendor/github.com/docker/distribution/registry.go
generated
vendored
2
vendor/github.com/docker/distribution/registry.go
generated
vendored
@@ -35,7 +35,7 @@ type Namespace interface {
|
||||
// reference.
|
||||
Repository(ctx context.Context, name reference.Named) (Repository, error)
|
||||
|
||||
// Repositories fills 'repos' with a lexigraphically sorted catalog of repositories
|
||||
// Repositories fills 'repos' with a lexicographically sorted catalog of repositories
|
||||
// up to the size of 'repos' and returns the value 'n' for the number of entries
|
||||
// which were filled. 'last' contains an offset in the catalog, and 'err' will be
|
||||
// set to io.EOF if there are no more entries to obtain.
|
||||
|
||||
2
vendor/github.com/docker/distribution/registry/api/v2/descriptors.go
generated
vendored
2
vendor/github.com/docker/distribution/registry/api/v2/descriptors.go
generated
vendored
@@ -4,9 +4,9 @@ import (
|
||||
"net/http"
|
||||
"regexp"
|
||||
|
||||
"github.com/docker/distribution/digest"
|
||||
"github.com/docker/distribution/reference"
|
||||
"github.com/docker/distribution/registry/api/errcode"
|
||||
"github.com/opencontainers/go-digest"
|
||||
)
|
||||
|
||||
var (
|
||||
|
||||
3
vendor/github.com/docker/distribution/registry/api/v2/urls.go
generated
vendored
3
vendor/github.com/docker/distribution/registry/api/v2/urls.go
generated
vendored
@@ -1,6 +1,7 @@
|
||||
package v2
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
@@ -149,6 +150,8 @@ func (ub *URLBuilder) BuildManifestURL(ref reference.Named) (string, error) {
|
||||
tagOrDigest = v.Tag()
|
||||
case reference.Digested:
|
||||
tagOrDigest = v.Digest().String()
|
||||
default:
|
||||
return "", fmt.Errorf("reference must have a tag or digest")
|
||||
}
|
||||
|
||||
manifestURL, err := route.URL("name", ref.Name(), "reference", tagOrDigest)
|
||||
|
||||
56
vendor/github.com/docker/distribution/registry/api/v2/urls_test.go
generated
vendored
56
vendor/github.com/docker/distribution/registry/api/v2/urls_test.go
generated
vendored
@@ -1,8 +1,10 @@
|
||||
package v2
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/distribution/reference"
|
||||
@@ -11,35 +13,48 @@ import (
|
||||
type urlBuilderTestCase struct {
|
||||
description string
|
||||
expectedPath string
|
||||
expectedErr error
|
||||
build func() (string, error)
|
||||
}
|
||||
|
||||
func makeURLBuilderTestCases(urlBuilder *URLBuilder) []urlBuilderTestCase {
|
||||
fooBarRef, _ := reference.ParseNamed("foo/bar")
|
||||
fooBarRef, _ := reference.WithName("foo/bar")
|
||||
return []urlBuilderTestCase{
|
||||
{
|
||||
description: "test base url",
|
||||
expectedPath: "/v2/",
|
||||
expectedErr: nil,
|
||||
build: urlBuilder.BuildBaseURL,
|
||||
},
|
||||
{
|
||||
description: "test tags url",
|
||||
expectedPath: "/v2/foo/bar/tags/list",
|
||||
expectedErr: nil,
|
||||
build: func() (string, error) {
|
||||
return urlBuilder.BuildTagsURL(fooBarRef)
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "test manifest url",
|
||||
description: "test manifest url tagged ref",
|
||||
expectedPath: "/v2/foo/bar/manifests/tag",
|
||||
expectedErr: nil,
|
||||
build: func() (string, error) {
|
||||
ref, _ := reference.WithTag(fooBarRef, "tag")
|
||||
return urlBuilder.BuildManifestURL(ref)
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "test manifest url bare ref",
|
||||
expectedPath: "",
|
||||
expectedErr: fmt.Errorf("reference must have a tag or digest"),
|
||||
build: func() (string, error) {
|
||||
return urlBuilder.BuildManifestURL(fooBarRef)
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "build blob url",
|
||||
expectedPath: "/v2/foo/bar/blobs/sha256:3b3692957d439ac1928219a83fac91e7bf96c153725526874673ae1f2023f8d5",
|
||||
expectedErr: nil,
|
||||
build: func() (string, error) {
|
||||
ref, _ := reference.WithDigest(fooBarRef, "sha256:3b3692957d439ac1928219a83fac91e7bf96c153725526874673ae1f2023f8d5")
|
||||
return urlBuilder.BuildBlobURL(ref)
|
||||
@@ -48,6 +63,7 @@ func makeURLBuilderTestCases(urlBuilder *URLBuilder) []urlBuilderTestCase {
|
||||
{
|
||||
description: "build blob upload url",
|
||||
expectedPath: "/v2/foo/bar/blobs/uploads/",
|
||||
expectedErr: nil,
|
||||
build: func() (string, error) {
|
||||
return urlBuilder.BuildBlobUploadURL(fooBarRef)
|
||||
},
|
||||
@@ -55,6 +71,7 @@ func makeURLBuilderTestCases(urlBuilder *URLBuilder) []urlBuilderTestCase {
|
||||
{
|
||||
description: "build blob upload url with digest and size",
|
||||
expectedPath: "/v2/foo/bar/blobs/uploads/?digest=sha256%3A3b3692957d439ac1928219a83fac91e7bf96c153725526874673ae1f2023f8d5&size=10000",
|
||||
expectedErr: nil,
|
||||
build: func() (string, error) {
|
||||
return urlBuilder.BuildBlobUploadURL(fooBarRef, url.Values{
|
||||
"size": []string{"10000"},
|
||||
@@ -65,6 +82,7 @@ func makeURLBuilderTestCases(urlBuilder *URLBuilder) []urlBuilderTestCase {
|
||||
{
|
||||
description: "build blob upload chunk url",
|
||||
expectedPath: "/v2/foo/bar/blobs/uploads/uuid-part",
|
||||
expectedErr: nil,
|
||||
build: func() (string, error) {
|
||||
return urlBuilder.BuildBlobUploadChunkURL(fooBarRef, "uuid-part")
|
||||
},
|
||||
@@ -72,6 +90,7 @@ func makeURLBuilderTestCases(urlBuilder *URLBuilder) []urlBuilderTestCase {
|
||||
{
|
||||
description: "build blob upload chunk url with digest and size",
|
||||
expectedPath: "/v2/foo/bar/blobs/uploads/uuid-part?digest=sha256%3A3b3692957d439ac1928219a83fac91e7bf96c153725526874673ae1f2023f8d5&size=10000",
|
||||
expectedErr: nil,
|
||||
build: func() (string, error) {
|
||||
return urlBuilder.BuildBlobUploadChunkURL(fooBarRef, "uuid-part", url.Values{
|
||||
"size": []string{"10000"},
|
||||
@@ -101,9 +120,14 @@ func TestURLBuilder(t *testing.T) {
|
||||
|
||||
for _, testCase := range makeURLBuilderTestCases(urlBuilder) {
|
||||
url, err := testCase.build()
|
||||
if err != nil {
|
||||
t.Fatalf("%s: error building url: %v", testCase.description, err)
|
||||
expectedErr := testCase.expectedErr
|
||||
if !reflect.DeepEqual(expectedErr, err) {
|
||||
t.Fatalf("%s: Expecting %v but got error %v", testCase.description, expectedErr, err)
|
||||
}
|
||||
if expectedErr != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
expectedURL := testCase.expectedPath
|
||||
if !relative {
|
||||
expectedURL = root + expectedURL
|
||||
@@ -136,8 +160,12 @@ func TestURLBuilderWithPrefix(t *testing.T) {
|
||||
|
||||
for _, testCase := range makeURLBuilderTestCases(urlBuilder) {
|
||||
url, err := testCase.build()
|
||||
if err != nil {
|
||||
t.Fatalf("%s: error building url: %v", testCase.description, err)
|
||||
expectedErr := testCase.expectedErr
|
||||
if !reflect.DeepEqual(expectedErr, err) {
|
||||
t.Fatalf("%s: Expecting %v but got error %v", testCase.description, expectedErr, err)
|
||||
}
|
||||
if expectedErr != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
expectedURL := testCase.expectedPath
|
||||
@@ -383,8 +411,12 @@ func TestBuilderFromRequest(t *testing.T) {
|
||||
|
||||
for _, testCase := range makeURLBuilderTestCases(builder) {
|
||||
buildURL, err := testCase.build()
|
||||
if err != nil {
|
||||
t.Fatalf("[relative=%t, request=%q, case=%q]: error building url: %v", relative, tr.name, testCase.description, err)
|
||||
expectedErr := testCase.expectedErr
|
||||
if !reflect.DeepEqual(expectedErr, err) {
|
||||
t.Fatalf("%s: Expecting %v but got error %v", testCase.description, expectedErr, err)
|
||||
}
|
||||
if expectedErr != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
expectedURL := testCase.expectedPath
|
||||
@@ -452,8 +484,12 @@ func TestBuilderFromRequestWithPrefix(t *testing.T) {
|
||||
|
||||
for _, testCase := range makeURLBuilderTestCases(builder) {
|
||||
buildURL, err := testCase.build()
|
||||
if err != nil {
|
||||
t.Fatalf("%s: error building url: %v", testCase.description, err)
|
||||
expectedErr := testCase.expectedErr
|
||||
if !reflect.DeepEqual(expectedErr, err) {
|
||||
t.Fatalf("%s: Expecting %v but got error %v", testCase.description, expectedErr, err)
|
||||
}
|
||||
if expectedErr != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
var expectedURL string
|
||||
|
||||
21
vendor/github.com/docker/distribution/registry/auth/token/token_test.go
generated
vendored
21
vendor/github.com/docker/distribution/registry/auth/token/token_test.go
generated
vendored
@@ -454,6 +454,27 @@ func TestAccessController(t *testing.T) {
|
||||
if userInfo.Name != "foo" {
|
||||
t.Fatalf("expected user name %q, got %q", "foo", userInfo.Name)
|
||||
}
|
||||
|
||||
// 5. Supply a token with full admin rights, which is represented as "*".
|
||||
token, err = makeTestToken(
|
||||
issuer, service,
|
||||
[]*ResourceActions{{
|
||||
Type: testAccess.Type,
|
||||
Name: testAccess.Name,
|
||||
Actions: []string{"*"},
|
||||
}},
|
||||
rootKeys[0], 1, time.Now(), time.Now().Add(5*time.Minute),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token.compactRaw()))
|
||||
|
||||
_, err = accessController.Authorized(ctx, testAccess)
|
||||
if err != nil {
|
||||
t.Fatalf("accessController returned unexpected error: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
// This tests that newAccessController can handle PEM blocks in the certificate
|
||||
|
||||
4
vendor/github.com/docker/distribution/registry/client/auth/session.go
generated
vendored
4
vendor/github.com/docker/distribution/registry/client/auth/session.go
generated
vendored
@@ -155,7 +155,9 @@ type RepositoryScope struct {
|
||||
// using the scope grammar
|
||||
func (rs RepositoryScope) String() string {
|
||||
repoType := "repository"
|
||||
if rs.Class != "" {
|
||||
// Keep existing format for image class to maintain backwards compatibility
|
||||
// with authorization servers which do not support the expanded grammar.
|
||||
if rs.Class != "" && rs.Class != "image" {
|
||||
repoType = fmt.Sprintf("%s(%s)", repoType, rs.Class)
|
||||
}
|
||||
return fmt.Sprintf("%s:%s:%s", repoType, rs.Repository, strings.Join(rs.Actions, ","))
|
||||
|
||||
10
vendor/github.com/docker/distribution/registry/client/repository.go
generated
vendored
10
vendor/github.com/docker/distribution/registry/client/repository.go
generated
vendored
@@ -15,12 +15,12 @@ import (
|
||||
|
||||
"github.com/docker/distribution"
|
||||
"github.com/docker/distribution/context"
|
||||
"github.com/docker/distribution/digest"
|
||||
"github.com/docker/distribution/reference"
|
||||
"github.com/docker/distribution/registry/api/v2"
|
||||
"github.com/docker/distribution/registry/client/transport"
|
||||
"github.com/docker/distribution/registry/storage/cache"
|
||||
"github.com/docker/distribution/registry/storage/cache/memory"
|
||||
"github.com/opencontainers/go-digest"
|
||||
)
|
||||
|
||||
// Registry provides an interface for calling Repositories, which returns a catalog of repositories.
|
||||
@@ -268,7 +268,7 @@ func descriptorFromResponse(response *http.Response) (distribution.Descriptor, e
|
||||
return desc, nil
|
||||
}
|
||||
|
||||
dgst, err := digest.ParseDigest(digestHeader)
|
||||
dgst, err := digest.Parse(digestHeader)
|
||||
if err != nil {
|
||||
return distribution.Descriptor{}, err
|
||||
}
|
||||
@@ -475,7 +475,7 @@ func (ms *manifests) Get(ctx context.Context, dgst digest.Digest, options ...dis
|
||||
return nil, distribution.ErrManifestNotModified
|
||||
} else if SuccessStatus(resp.StatusCode) {
|
||||
if contentDgst != nil {
|
||||
dgst, err := digest.ParseDigest(resp.Header.Get("Docker-Content-Digest"))
|
||||
dgst, err := digest.Parse(resp.Header.Get("Docker-Content-Digest"))
|
||||
if err == nil {
|
||||
*contentDgst = dgst
|
||||
}
|
||||
@@ -553,7 +553,7 @@ func (ms *manifests) Put(ctx context.Context, m distribution.Manifest, options .
|
||||
|
||||
if SuccessStatus(resp.StatusCode) {
|
||||
dgstHeader := resp.Header.Get("Docker-Content-Digest")
|
||||
dgst, err := digest.ParseDigest(dgstHeader)
|
||||
dgst, err := digest.Parse(dgstHeader)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@@ -661,7 +661,7 @@ func (bs *blobs) Put(ctx context.Context, mediaType string, p []byte) (distribut
|
||||
if err != nil {
|
||||
return distribution.Descriptor{}, err
|
||||
}
|
||||
dgstr := digest.Canonical.New()
|
||||
dgstr := digest.Canonical.Digester()
|
||||
n, err := io.Copy(writer, io.TeeReader(bytes.NewReader(p), dgstr.Hash()))
|
||||
if err != nil {
|
||||
return distribution.Descriptor{}, err
|
||||
|
||||
34
vendor/github.com/docker/distribution/registry/client/repository_test.go
generated
vendored
34
vendor/github.com/docker/distribution/registry/client/repository_test.go
generated
vendored
@@ -16,7 +16,6 @@ import (
|
||||
|
||||
"github.com/docker/distribution"
|
||||
"github.com/docker/distribution/context"
|
||||
"github.com/docker/distribution/digest"
|
||||
"github.com/docker/distribution/manifest"
|
||||
"github.com/docker/distribution/manifest/schema1"
|
||||
"github.com/docker/distribution/reference"
|
||||
@@ -25,6 +24,7 @@ import (
|
||||
"github.com/docker/distribution/testutil"
|
||||
"github.com/docker/distribution/uuid"
|
||||
"github.com/docker/libtrust"
|
||||
"github.com/opencontainers/go-digest"
|
||||
)
|
||||
|
||||
func testServer(rrm testutil.RequestResponseMap) (string, func()) {
|
||||
@@ -100,7 +100,7 @@ func addTestCatalog(route string, content []byte, link string, m *testutil.Reque
|
||||
func TestBlobDelete(t *testing.T) {
|
||||
dgst, _ := newRandomBlob(1024)
|
||||
var m testutil.RequestResponseMap
|
||||
repo, _ := reference.ParseNamed("test.example.com/repo1")
|
||||
repo, _ := reference.WithName("test.example.com/repo1")
|
||||
m = append(m, testutil.RequestResponseMapping{
|
||||
Request: testutil.Request{
|
||||
Method: "DELETE",
|
||||
@@ -139,7 +139,7 @@ func TestBlobFetch(t *testing.T) {
|
||||
defer c()
|
||||
|
||||
ctx := context.Background()
|
||||
repo, _ := reference.ParseNamed("test.example.com/repo1")
|
||||
repo, _ := reference.WithName("test.example.com/repo1")
|
||||
r, err := NewRepository(ctx, repo, e, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -160,7 +160,7 @@ func TestBlobFetch(t *testing.T) {
|
||||
func TestBlobExistsNoContentLength(t *testing.T) {
|
||||
var m testutil.RequestResponseMap
|
||||
|
||||
repo, _ := reference.ParseNamed("biff")
|
||||
repo, _ := reference.WithName("biff")
|
||||
dgst, content := newRandomBlob(1024)
|
||||
m = append(m, testutil.RequestResponseMapping{
|
||||
Request: testutil.Request{
|
||||
@@ -219,7 +219,7 @@ func TestBlobExists(t *testing.T) {
|
||||
defer c()
|
||||
|
||||
ctx := context.Background()
|
||||
repo, _ := reference.ParseNamed("test.example.com/repo1")
|
||||
repo, _ := reference.WithName("test.example.com/repo1")
|
||||
r, err := NewRepository(ctx, repo, e, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -251,7 +251,7 @@ func TestBlobUploadChunked(t *testing.T) {
|
||||
b1[512:513],
|
||||
b1[513:1024],
|
||||
}
|
||||
repo, _ := reference.ParseNamed("test.example.com/uploadrepo")
|
||||
repo, _ := reference.WithName("test.example.com/uploadrepo")
|
||||
uuids := []string{uuid.Generate().String()}
|
||||
m = append(m, testutil.RequestResponseMapping{
|
||||
Request: testutil.Request{
|
||||
@@ -366,7 +366,7 @@ func TestBlobUploadChunked(t *testing.T) {
|
||||
func TestBlobUploadMonolithic(t *testing.T) {
|
||||
dgst, b1 := newRandomBlob(1024)
|
||||
var m testutil.RequestResponseMap
|
||||
repo, _ := reference.ParseNamed("test.example.com/uploadrepo")
|
||||
repo, _ := reference.WithName("test.example.com/uploadrepo")
|
||||
uploadID := uuid.Generate().String()
|
||||
m = append(m, testutil.RequestResponseMapping{
|
||||
Request: testutil.Request{
|
||||
@@ -474,9 +474,9 @@ func TestBlobUploadMonolithic(t *testing.T) {
|
||||
func TestBlobMount(t *testing.T) {
|
||||
dgst, content := newRandomBlob(1024)
|
||||
var m testutil.RequestResponseMap
|
||||
repo, _ := reference.ParseNamed("test.example.com/uploadrepo")
|
||||
repo, _ := reference.WithName("test.example.com/uploadrepo")
|
||||
|
||||
sourceRepo, _ := reference.ParseNamed("test.example.com/sourcerepo")
|
||||
sourceRepo, _ := reference.WithName("test.example.com/sourcerepo")
|
||||
canonicalRef, _ := reference.WithDigest(sourceRepo, dgst)
|
||||
|
||||
m = append(m, testutil.RequestResponseMapping{
|
||||
@@ -678,7 +678,7 @@ func checkEqualManifest(m1, m2 *schema1.SignedManifest) error {
|
||||
|
||||
func TestV1ManifestFetch(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
repo, _ := reference.ParseNamed("test.example.com/repo")
|
||||
repo, _ := reference.WithName("test.example.com/repo")
|
||||
m1, dgst, _ := newRandomSchemaV1Manifest(repo, "latest", 6)
|
||||
var m testutil.RequestResponseMap
|
||||
_, pl, err := m1.Payload()
|
||||
@@ -755,7 +755,7 @@ func TestV1ManifestFetch(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestManifestFetchWithEtag(t *testing.T) {
|
||||
repo, _ := reference.ParseNamed("test.example.com/repo/by/tag")
|
||||
repo, _ := reference.WithName("test.example.com/repo/by/tag")
|
||||
_, d1, p1 := newRandomSchemaV1Manifest(repo, "latest", 6)
|
||||
var m testutil.RequestResponseMap
|
||||
addTestManifestWithEtag(repo, "latest", p1, &m, d1.String())
|
||||
@@ -785,7 +785,7 @@ func TestManifestFetchWithEtag(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestManifestDelete(t *testing.T) {
|
||||
repo, _ := reference.ParseNamed("test.example.com/repo/delete")
|
||||
repo, _ := reference.WithName("test.example.com/repo/delete")
|
||||
_, dgst1, _ := newRandomSchemaV1Manifest(repo, "latest", 6)
|
||||
_, dgst2, _ := newRandomSchemaV1Manifest(repo, "latest", 6)
|
||||
var m testutil.RequestResponseMap
|
||||
@@ -825,7 +825,7 @@ func TestManifestDelete(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestManifestPut(t *testing.T) {
|
||||
repo, _ := reference.ParseNamed("test.example.com/repo/delete")
|
||||
repo, _ := reference.WithName("test.example.com/repo/delete")
|
||||
m1, dgst, _ := newRandomSchemaV1Manifest(repo, "other", 6)
|
||||
|
||||
_, payload, err := m1.Payload()
|
||||
@@ -890,7 +890,7 @@ func TestManifestPut(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestManifestTags(t *testing.T) {
|
||||
repo, _ := reference.ParseNamed("test.example.com/repo/tags/list")
|
||||
repo, _ := reference.WithName("test.example.com/repo/tags/list")
|
||||
tagsList := []byte(strings.TrimSpace(`
|
||||
{
|
||||
"name": "test.example.com/repo/tags/list",
|
||||
@@ -952,7 +952,7 @@ func TestManifestTags(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestObtainsErrorForMissingTag(t *testing.T) {
|
||||
repo, _ := reference.ParseNamed("test.example.com/repo")
|
||||
repo, _ := reference.WithName("test.example.com/repo")
|
||||
|
||||
var m testutil.RequestResponseMap
|
||||
var errors errcode.Errors
|
||||
@@ -998,7 +998,7 @@ func TestManifestTagsPaginated(t *testing.T) {
|
||||
s := httptest.NewServer(http.NotFoundHandler())
|
||||
defer s.Close()
|
||||
|
||||
repo, _ := reference.ParseNamed("test.example.com/repo/tags/list")
|
||||
repo, _ := reference.WithName("test.example.com/repo/tags/list")
|
||||
tagsList := []string{"tag1", "tag2", "funtag"}
|
||||
var m testutil.RequestResponseMap
|
||||
for i := 0; i < 3; i++ {
|
||||
@@ -1067,7 +1067,7 @@ func TestManifestTagsPaginated(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestManifestUnauthorized(t *testing.T) {
|
||||
repo, _ := reference.ParseNamed("test.example.com/repo")
|
||||
repo, _ := reference.WithName("test.example.com/repo")
|
||||
_, dgst, _ := newRandomSchemaV1Manifest(repo, "latest", 6)
|
||||
var m testutil.RequestResponseMap
|
||||
|
||||
|
||||
160
vendor/github.com/docker/distribution/registry/handlers/api_test.go
generated
vendored
160
vendor/github.com/docker/distribution/registry/handlers/api_test.go
generated
vendored
@@ -3,6 +3,7 @@ package handlers
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
@@ -21,7 +22,6 @@ import (
|
||||
"github.com/docker/distribution"
|
||||
"github.com/docker/distribution/configuration"
|
||||
"github.com/docker/distribution/context"
|
||||
"github.com/docker/distribution/digest"
|
||||
"github.com/docker/distribution/manifest"
|
||||
"github.com/docker/distribution/manifest/manifestlist"
|
||||
"github.com/docker/distribution/manifest/schema1"
|
||||
@@ -29,16 +29,24 @@ import (
|
||||
"github.com/docker/distribution/reference"
|
||||
"github.com/docker/distribution/registry/api/errcode"
|
||||
"github.com/docker/distribution/registry/api/v2"
|
||||
storagedriver "github.com/docker/distribution/registry/storage/driver"
|
||||
"github.com/docker/distribution/registry/storage/driver/factory"
|
||||
_ "github.com/docker/distribution/registry/storage/driver/testdriver"
|
||||
"github.com/docker/distribution/testutil"
|
||||
"github.com/docker/libtrust"
|
||||
"github.com/gorilla/handlers"
|
||||
"github.com/opencontainers/go-digest"
|
||||
)
|
||||
|
||||
var headerConfig = http.Header{
|
||||
"X-Content-Type-Options": []string{"nosniff"},
|
||||
}
|
||||
|
||||
const (
|
||||
// digestSha256EmptyTar is the canonical sha256 digest of empty data
|
||||
digestSha256EmptyTar = "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
|
||||
)
|
||||
|
||||
// TestCheckAPI hits the base endpoint (/v2/) ensures we return the specified
|
||||
// 200 OK response.
|
||||
func TestCheckAPI(t *testing.T) {
|
||||
@@ -272,7 +280,7 @@ func makeBlobArgs(t *testing.T) blobArgs {
|
||||
layerFile: layerFile,
|
||||
layerDigest: layerDigest,
|
||||
}
|
||||
args.imageName, _ = reference.ParseNamed("foo/bar")
|
||||
args.imageName, _ = reference.WithName("foo/bar")
|
||||
return args
|
||||
}
|
||||
|
||||
@@ -514,7 +522,7 @@ func testBlobAPI(t *testing.T, env *testEnv, args blobArgs) *testEnv {
|
||||
// Now, push just a chunk
|
||||
layerFile.Seek(0, 0)
|
||||
|
||||
canonicalDigester := digest.Canonical.New()
|
||||
canonicalDigester := digest.Canonical.Digester()
|
||||
if _, err := io.Copy(canonicalDigester.Hash(), layerFile); err != nil {
|
||||
t.Fatalf("error copying to digest: %v", err)
|
||||
}
|
||||
@@ -552,10 +560,7 @@ func testBlobAPI(t *testing.T, env *testEnv, args blobArgs) *testEnv {
|
||||
})
|
||||
|
||||
// Verify the body
|
||||
verifier, err := digest.NewDigestVerifier(layerDigest)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error getting digest verifier: %s", err)
|
||||
}
|
||||
verifier := layerDigest.Verifier()
|
||||
io.Copy(verifier, resp.Body)
|
||||
|
||||
if !verifier.Verified() {
|
||||
@@ -675,7 +680,7 @@ func testBlobDelete(t *testing.T, env *testEnv, args blobArgs) {
|
||||
pushLayer(t, env.builder, imageName, layerDigest, uploadURLBase, layerFile)
|
||||
|
||||
layerFile.Seek(0, os.SEEK_SET)
|
||||
canonicalDigester := digest.Canonical.New()
|
||||
canonicalDigester := digest.Canonical.Digester()
|
||||
if _, err := io.Copy(canonicalDigester.Hash(), layerFile); err != nil {
|
||||
t.Fatalf("error copying to digest: %v", err)
|
||||
}
|
||||
@@ -700,7 +705,7 @@ func TestDeleteDisabled(t *testing.T) {
|
||||
env := newTestEnv(t, false)
|
||||
defer env.Shutdown()
|
||||
|
||||
imageName, _ := reference.ParseNamed("foo/bar")
|
||||
imageName, _ := reference.WithName("foo/bar")
|
||||
// "build" our layer file
|
||||
layerFile, layerDigest, err := testutil.CreateRandomTarFile()
|
||||
if err != nil {
|
||||
@@ -727,7 +732,7 @@ func TestDeleteReadOnly(t *testing.T) {
|
||||
env := newTestEnv(t, true)
|
||||
defer env.Shutdown()
|
||||
|
||||
imageName, _ := reference.ParseNamed("foo/bar")
|
||||
imageName, _ := reference.WithName("foo/bar")
|
||||
// "build" our layer file
|
||||
layerFile, layerDigest, err := testutil.CreateRandomTarFile()
|
||||
if err != nil {
|
||||
@@ -757,7 +762,7 @@ func TestStartPushReadOnly(t *testing.T) {
|
||||
defer env.Shutdown()
|
||||
env.app.readOnly = true
|
||||
|
||||
imageName, _ := reference.ParseNamed("foo/bar")
|
||||
imageName, _ := reference.WithName("foo/bar")
|
||||
|
||||
layerUploadURL, err := env.builder.BuildBlobUploadURL(imageName)
|
||||
if err != nil {
|
||||
@@ -795,8 +800,8 @@ type manifestArgs struct {
|
||||
}
|
||||
|
||||
func TestManifestAPI(t *testing.T) {
|
||||
schema1Repo, _ := reference.ParseNamed("foo/schema1")
|
||||
schema2Repo, _ := reference.ParseNamed("foo/schema2")
|
||||
schema1Repo, _ := reference.WithName("foo/schema1")
|
||||
schema2Repo, _ := reference.WithName("foo/schema2")
|
||||
|
||||
deleteEnabled := false
|
||||
env1 := newTestEnv(t, deleteEnabled)
|
||||
@@ -813,9 +818,96 @@ func TestManifestAPI(t *testing.T) {
|
||||
testManifestAPIManifestList(t, env2, schema2Args)
|
||||
}
|
||||
|
||||
// storageManifestErrDriverFactory implements the factory.StorageDriverFactory interface.
|
||||
type storageManifestErrDriverFactory struct{}
|
||||
|
||||
const (
|
||||
repositoryWithManifestNotFound = "manifesttagnotfound"
|
||||
repositoryWithManifestInvalidPath = "manifestinvalidpath"
|
||||
repositoryWithManifestBadLink = "manifestbadlink"
|
||||
repositoryWithGenericStorageError = "genericstorageerr"
|
||||
)
|
||||
|
||||
func (factory *storageManifestErrDriverFactory) Create(parameters map[string]interface{}) (storagedriver.StorageDriver, error) {
|
||||
// Initialize the mock driver
|
||||
var errGenericStorage = errors.New("generic storage error")
|
||||
return &mockErrorDriver{
|
||||
returnErrs: []mockErrorMapping{
|
||||
{
|
||||
pathMatch: fmt.Sprintf("%s/_manifests/tags", repositoryWithManifestNotFound),
|
||||
content: nil,
|
||||
err: storagedriver.PathNotFoundError{},
|
||||
},
|
||||
{
|
||||
pathMatch: fmt.Sprintf("%s/_manifests/tags", repositoryWithManifestInvalidPath),
|
||||
content: nil,
|
||||
err: storagedriver.InvalidPathError{},
|
||||
},
|
||||
{
|
||||
pathMatch: fmt.Sprintf("%s/_manifests/tags", repositoryWithManifestBadLink),
|
||||
content: []byte("this is a bad sha"),
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
pathMatch: fmt.Sprintf("%s/_manifests/tags", repositoryWithGenericStorageError),
|
||||
content: nil,
|
||||
err: errGenericStorage,
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
type mockErrorMapping struct {
|
||||
pathMatch string
|
||||
content []byte
|
||||
err error
|
||||
}
|
||||
|
||||
// mockErrorDriver implements StorageDriver to force storage error on manifest request
|
||||
type mockErrorDriver struct {
|
||||
storagedriver.StorageDriver
|
||||
returnErrs []mockErrorMapping
|
||||
}
|
||||
|
||||
func (dr *mockErrorDriver) GetContent(ctx context.Context, path string) ([]byte, error) {
|
||||
for _, returns := range dr.returnErrs {
|
||||
if strings.Contains(path, returns.pathMatch) {
|
||||
return returns.content, returns.err
|
||||
}
|
||||
}
|
||||
return nil, errors.New("Unknown storage error")
|
||||
}
|
||||
|
||||
func TestGetManifestWithStorageError(t *testing.T) {
|
||||
factory.Register("storagemanifesterror", &storageManifestErrDriverFactory{})
|
||||
config := configuration.Configuration{
|
||||
Storage: configuration.Storage{
|
||||
"storagemanifesterror": configuration.Parameters{},
|
||||
"maintenance": configuration.Parameters{"uploadpurging": map[interface{}]interface{}{
|
||||
"enabled": false,
|
||||
}},
|
||||
},
|
||||
}
|
||||
config.HTTP.Headers = headerConfig
|
||||
env1 := newTestEnvWithConfig(t, &config)
|
||||
defer env1.Shutdown()
|
||||
|
||||
repo, _ := reference.WithName(repositoryWithManifestNotFound)
|
||||
testManifestWithStorageError(t, env1, repo, http.StatusNotFound, v2.ErrorCodeManifestUnknown)
|
||||
|
||||
repo, _ = reference.WithName(repositoryWithGenericStorageError)
|
||||
testManifestWithStorageError(t, env1, repo, http.StatusInternalServerError, errcode.ErrorCodeUnknown)
|
||||
|
||||
repo, _ = reference.WithName(repositoryWithManifestInvalidPath)
|
||||
testManifestWithStorageError(t, env1, repo, http.StatusInternalServerError, errcode.ErrorCodeUnknown)
|
||||
|
||||
repo, _ = reference.WithName(repositoryWithManifestBadLink)
|
||||
testManifestWithStorageError(t, env1, repo, http.StatusInternalServerError, errcode.ErrorCodeUnknown)
|
||||
}
|
||||
|
||||
func TestManifestDelete(t *testing.T) {
|
||||
schema1Repo, _ := reference.ParseNamed("foo/schema1")
|
||||
schema2Repo, _ := reference.ParseNamed("foo/schema2")
|
||||
schema1Repo, _ := reference.WithName("foo/schema1")
|
||||
schema2Repo, _ := reference.WithName("foo/schema2")
|
||||
|
||||
deleteEnabled := true
|
||||
env := newTestEnv(t, deleteEnabled)
|
||||
@@ -827,7 +919,7 @@ func TestManifestDelete(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestManifestDeleteDisabled(t *testing.T) {
|
||||
schema1Repo, _ := reference.ParseNamed("foo/schema1")
|
||||
schema1Repo, _ := reference.WithName("foo/schema1")
|
||||
deleteEnabled := false
|
||||
env := newTestEnv(t, deleteEnabled)
|
||||
defer env.Shutdown()
|
||||
@@ -835,7 +927,7 @@ func TestManifestDeleteDisabled(t *testing.T) {
|
||||
}
|
||||
|
||||
func testManifestDeleteDisabled(t *testing.T, env *testEnv, imageName reference.Named) {
|
||||
ref, _ := reference.WithDigest(imageName, digest.DigestSha256EmptyTar)
|
||||
ref, _ := reference.WithDigest(imageName, digestSha256EmptyTar)
|
||||
manifestURL, err := env.builder.BuildManifestURL(ref)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error getting manifest url: %v", err)
|
||||
@@ -850,6 +942,26 @@ func testManifestDeleteDisabled(t *testing.T, env *testEnv, imageName reference.
|
||||
checkResponse(t, "status of disabled delete of manifest", resp, http.StatusMethodNotAllowed)
|
||||
}
|
||||
|
||||
func testManifestWithStorageError(t *testing.T, env *testEnv, imageName reference.Named, expectedStatusCode int, expectedErrorCode errcode.ErrorCode) {
|
||||
tag := "latest"
|
||||
tagRef, _ := reference.WithTag(imageName, tag)
|
||||
manifestURL, err := env.builder.BuildManifestURL(tagRef)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error getting manifest url: %v", err)
|
||||
}
|
||||
|
||||
// -----------------------------
|
||||
// Attempt to fetch the manifest
|
||||
resp, err := http.Get(manifestURL)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error getting manifest: %v", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
checkResponse(t, "getting non-existent manifest", resp, expectedStatusCode)
|
||||
checkBodyHasErrorCodes(t, "getting non-existent manifest", resp, expectedErrorCode)
|
||||
return
|
||||
}
|
||||
|
||||
func testManifestAPISchema1(t *testing.T, env *testEnv, imageName reference.Named) manifestArgs {
|
||||
tag := "thetag"
|
||||
args := manifestArgs{imageName: imageName}
|
||||
@@ -1218,7 +1330,7 @@ func testManifestAPISchema2(t *testing.T, env *testEnv, imageName reference.Name
|
||||
Config: distribution.Descriptor{
|
||||
Digest: "sha256:1a9ec845ee94c202b2d5da74a24f0ed2058318bfa9879fa541efaecba272e86b",
|
||||
Size: 3253,
|
||||
MediaType: schema2.MediaTypeConfig,
|
||||
MediaType: schema2.MediaTypeImageConfig,
|
||||
},
|
||||
Layers: []distribution.Descriptor{
|
||||
{
|
||||
@@ -2075,7 +2187,7 @@ func doPushLayer(t *testing.T, ub *v2.URLBuilder, name reference.Named, dgst dig
|
||||
|
||||
// pushLayer pushes the layer content returning the url on success.
|
||||
func pushLayer(t *testing.T, ub *v2.URLBuilder, name reference.Named, dgst digest.Digest, uploadURLBase string, body io.Reader) string {
|
||||
digester := digest.Canonical.New()
|
||||
digester := digest.Canonical.Digester()
|
||||
|
||||
resp, err := doPushLayer(t, ub, name, dgst, uploadURLBase, io.TeeReader(body, digester.Hash()))
|
||||
if err != nil {
|
||||
@@ -2142,7 +2254,7 @@ func doPushChunk(t *testing.T, uploadURLBase string, body io.Reader) (*http.Resp
|
||||
|
||||
uploadURL := u.String()
|
||||
|
||||
digester := digest.Canonical.New()
|
||||
digester := digest.Canonical.Digester()
|
||||
|
||||
req, err := http.NewRequest("PATCH", uploadURL, io.TeeReader(body, digester.Hash()))
|
||||
if err != nil {
|
||||
@@ -2291,7 +2403,7 @@ func checkErr(t *testing.T, err error, msg string) {
|
||||
}
|
||||
|
||||
func createRepository(env *testEnv, t *testing.T, imageName string, tag string) digest.Digest {
|
||||
imageNameRef, err := reference.ParseNamed(imageName)
|
||||
imageNameRef, err := reference.WithName(imageName)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to parse reference: %v", err)
|
||||
}
|
||||
@@ -2362,7 +2474,7 @@ func TestRegistryAsCacheMutationAPIs(t *testing.T) {
|
||||
env := newTestEnvMirror(t, deleteEnabled)
|
||||
defer env.Shutdown()
|
||||
|
||||
imageName, _ := reference.ParseNamed("foo/bar")
|
||||
imageName, _ := reference.WithName("foo/bar")
|
||||
tag := "latest"
|
||||
tagRef, _ := reference.WithTag(imageName, tag)
|
||||
manifestURL, err := env.builder.BuildManifestURL(tagRef)
|
||||
@@ -2408,7 +2520,7 @@ func TestRegistryAsCacheMutationAPIs(t *testing.T) {
|
||||
checkResponse(t, fmt.Sprintf("starting layer push to cache %v", imageName), resp, errcode.ErrorCodeUnsupported.Descriptor().HTTPStatusCode)
|
||||
|
||||
// Blob Delete
|
||||
ref, _ := reference.WithDigest(imageName, digest.DigestSha256EmptyTar)
|
||||
ref, _ := reference.WithDigest(imageName, digestSha256EmptyTar)
|
||||
blobURL, err := env.builder.BuildBlobURL(ref)
|
||||
resp, err = httpDelete(blobURL)
|
||||
checkResponse(t, "deleting blob from cache", resp, errcode.ErrorCodeUnsupported.Descriptor().HTTPStatusCode)
|
||||
@@ -2455,7 +2567,7 @@ func TestProxyManifestGetByTag(t *testing.T) {
|
||||
}
|
||||
truthConfig.HTTP.Headers = headerConfig
|
||||
|
||||
imageName, _ := reference.ParseNamed("foo/bar")
|
||||
imageName, _ := reference.WithName("foo/bar")
|
||||
tag := "latest"
|
||||
|
||||
truthEnv := newTestEnvWithConfig(t, &truthConfig)
|
||||
|
||||
41
vendor/github.com/docker/distribution/registry/handlers/app.go
generated
vendored
41
vendor/github.com/docker/distribution/registry/handlers/app.go
generated
vendored
@@ -100,7 +100,7 @@ func NewApp(ctx context.Context, config *configuration.Configuration) *App {
|
||||
app.register(v2.RouteNameBase, func(ctx *Context, r *http.Request) http.Handler {
|
||||
return http.HandlerFunc(apiBase)
|
||||
})
|
||||
app.register(v2.RouteNameManifest, imageManifestDispatcher)
|
||||
app.register(v2.RouteNameManifest, manifestDispatcher)
|
||||
app.register(v2.RouteNameCatalog, catalogDispatcher)
|
||||
app.register(v2.RouteNameTags, tagsDispatcher)
|
||||
app.register(v2.RouteNameBlob, blobDispatcher)
|
||||
@@ -213,6 +213,10 @@ func NewApp(ctx context.Context, config *configuration.Configuration) *App {
|
||||
options = append(options, storage.EnableRedirect)
|
||||
}
|
||||
|
||||
if !config.Validation.Enabled {
|
||||
config.Validation.Enabled = !config.Validation.Disabled
|
||||
}
|
||||
|
||||
// configure validation
|
||||
if config.Validation.Enabled {
|
||||
if len(config.Validation.Manifests.URLs.Allow) == 0 && len(config.Validation.Manifests.URLs.Deny) == 0 {
|
||||
@@ -592,24 +596,19 @@ func (app *App) configureSecret(configuration *configuration.Configuration) {
|
||||
func (app *App) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
defer r.Body.Close() // ensure that request body is always closed.
|
||||
|
||||
// Instantiate an http context here so we can track the error codes
|
||||
// returned by the request router.
|
||||
ctx := defaultContextManager.context(app, w, r)
|
||||
// Prepare the context with our own little decorations.
|
||||
ctx := r.Context()
|
||||
ctx = ctxu.WithRequest(ctx, r)
|
||||
ctx, w = ctxu.WithResponseWriter(ctx, w)
|
||||
ctx = ctxu.WithLogger(ctx, ctxu.GetRequestLogger(ctx))
|
||||
r = r.WithContext(ctx)
|
||||
|
||||
defer func() {
|
||||
status, ok := ctx.Value("http.response.status").(int)
|
||||
if ok && status >= 200 && status <= 399 {
|
||||
ctxu.GetResponseLogger(ctx).Infof("response completed")
|
||||
ctxu.GetResponseLogger(r.Context()).Infof("response completed")
|
||||
}
|
||||
}()
|
||||
defer defaultContextManager.release(ctx)
|
||||
|
||||
// NOTE(stevvooe): Total hack to get instrumented responsewriter from context.
|
||||
var err error
|
||||
w, err = ctxu.GetResponseWriter(ctx)
|
||||
if err != nil {
|
||||
ctxu.GetLogger(ctx).Warnf("response writer not found in context")
|
||||
}
|
||||
|
||||
// Set a header with the Docker Distribution API Version for all responses.
|
||||
w.Header().Add("Docker-Distribution-API-Version", "registry/2.0")
|
||||
@@ -645,8 +644,11 @@ func (app *App) dispatcher(dispatch dispatchFunc) http.Handler {
|
||||
// Add username to request logging
|
||||
context.Context = ctxu.WithLogger(context.Context, ctxu.GetLogger(context.Context, auth.UserNameKey))
|
||||
|
||||
// sync up context on the request.
|
||||
r = r.WithContext(context)
|
||||
|
||||
if app.nameRequired(r) {
|
||||
nameRef, err := reference.ParseNamed(getName(context))
|
||||
nameRef, err := reference.WithName(getName(context))
|
||||
if err != nil {
|
||||
ctxu.GetLogger(context).Errorf("error parsing reference from context: %v", err)
|
||||
context.Errors = append(context.Errors, distribution.ErrRepositoryNameInvalid{
|
||||
@@ -752,7 +754,7 @@ func (app *App) logError(context context.Context, errors errcode.Errors) {
|
||||
// context constructs the context object for the application. This only be
|
||||
// called once per request.
|
||||
func (app *App) context(w http.ResponseWriter, r *http.Request) *Context {
|
||||
ctx := defaultContextManager.context(app, w, r)
|
||||
ctx := r.Context()
|
||||
ctx = ctxu.WithVars(ctx, r)
|
||||
ctx = ctxu.WithLogger(ctx, ctxu.GetLogger(ctx,
|
||||
"vars.name",
|
||||
@@ -857,8 +859,11 @@ func (app *App) eventBridge(ctx *Context, r *http.Request) notifications.Listene
|
||||
// nameRequired returns true if the route requires a name.
|
||||
func (app *App) nameRequired(r *http.Request) bool {
|
||||
route := mux.CurrentRoute(r)
|
||||
if route == nil {
|
||||
return true
|
||||
}
|
||||
routeName := route.GetName()
|
||||
return route == nil || (routeName != v2.RouteNameBase && routeName != v2.RouteNameCatalog)
|
||||
return routeName != v2.RouteNameBase && routeName != v2.RouteNameCatalog
|
||||
}
|
||||
|
||||
// apiBase implements a simple yes-man for doing overall checks against the
|
||||
@@ -897,12 +902,10 @@ func appendAccessRecords(records []auth.Access, method string, repo string) []au
|
||||
Action: "push",
|
||||
})
|
||||
case "DELETE":
|
||||
// DELETE access requires full admin rights, which is represented
|
||||
// as "*". This may not be ideal.
|
||||
records = append(records,
|
||||
auth.Access{
|
||||
Resource: resource,
|
||||
Action: "*",
|
||||
Action: "delete",
|
||||
})
|
||||
}
|
||||
return records
|
||||
|
||||
6
vendor/github.com/docker/distribution/registry/handlers/app_test.go
generated
vendored
6
vendor/github.com/docker/distribution/registry/handlers/app_test.go
generated
vendored
@@ -229,9 +229,9 @@ func TestAppendAccessRecords(t *testing.T) {
|
||||
Resource: expectedResource,
|
||||
Action: "push",
|
||||
}
|
||||
expectedAllRecord := auth.Access{
|
||||
expectedDeleteRecord := auth.Access{
|
||||
Resource: expectedResource,
|
||||
Action: "*",
|
||||
Action: "delete",
|
||||
}
|
||||
|
||||
records := []auth.Access{}
|
||||
@@ -271,7 +271,7 @@ func TestAppendAccessRecords(t *testing.T) {
|
||||
|
||||
records = []auth.Access{}
|
||||
result = appendAccessRecords(records, "DELETE", repo)
|
||||
expectedResult = []auth.Access{expectedAllRecord}
|
||||
expectedResult = []auth.Access{expectedDeleteRecord}
|
||||
if ok := reflect.DeepEqual(result, expectedResult); !ok {
|
||||
t.Fatalf("Actual access record differs from expected")
|
||||
}
|
||||
|
||||
2
vendor/github.com/docker/distribution/registry/handlers/blob.go
generated
vendored
2
vendor/github.com/docker/distribution/registry/handlers/blob.go
generated
vendored
@@ -5,10 +5,10 @@ import (
|
||||
|
||||
"github.com/docker/distribution"
|
||||
"github.com/docker/distribution/context"
|
||||
"github.com/docker/distribution/digest"
|
||||
"github.com/docker/distribution/registry/api/errcode"
|
||||
"github.com/docker/distribution/registry/api/v2"
|
||||
"github.com/gorilla/handlers"
|
||||
"github.com/opencontainers/go-digest"
|
||||
)
|
||||
|
||||
// blobDispatcher uses the request context to build a blobHandler.
|
||||
|
||||
8
vendor/github.com/docker/distribution/registry/handlers/blobupload.go
generated
vendored
8
vendor/github.com/docker/distribution/registry/handlers/blobupload.go
generated
vendored
@@ -7,12 +7,12 @@ import (
|
||||
|
||||
"github.com/docker/distribution"
|
||||
ctxu "github.com/docker/distribution/context"
|
||||
"github.com/docker/distribution/digest"
|
||||
"github.com/docker/distribution/reference"
|
||||
"github.com/docker/distribution/registry/api/errcode"
|
||||
"github.com/docker/distribution/registry/api/v2"
|
||||
"github.com/docker/distribution/registry/storage"
|
||||
"github.com/gorilla/handlers"
|
||||
"github.com/opencontainers/go-digest"
|
||||
)
|
||||
|
||||
// blobUploadDispatcher constructs and returns the blob upload handler for the
|
||||
@@ -211,7 +211,7 @@ func (buh *blobUploadHandler) PutBlobUploadComplete(w http.ResponseWriter, r *ht
|
||||
return
|
||||
}
|
||||
|
||||
dgst, err := digest.ParseDigest(dgstStr)
|
||||
dgst, err := digest.Parse(dgstStr)
|
||||
if err != nil {
|
||||
// no digest? return error, but allow retry.
|
||||
buh.Errors = append(buh.Errors, v2.ErrorCodeDigestInvalid.WithDetail("digest parsing failed"))
|
||||
@@ -329,12 +329,12 @@ func (buh *blobUploadHandler) blobUploadResponse(w http.ResponseWriter, r *http.
|
||||
// successful, the blob is linked into the blob store and 201 Created is
|
||||
// returned with the canonical url of the blob.
|
||||
func (buh *blobUploadHandler) createBlobMountOption(fromRepo, mountDigest string) (distribution.BlobCreateOption, error) {
|
||||
dgst, err := digest.ParseDigest(mountDigest)
|
||||
dgst, err := digest.Parse(mountDigest)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ref, err := reference.ParseNamed(fromRepo)
|
||||
ref, err := reference.WithName(fromRepo)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
64
vendor/github.com/docker/distribution/registry/handlers/context.go
generated
vendored
64
vendor/github.com/docker/distribution/registry/handlers/context.go
generated
vendored
@@ -3,14 +3,13 @@ package handlers
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"sync"
|
||||
|
||||
"github.com/docker/distribution"
|
||||
ctxu "github.com/docker/distribution/context"
|
||||
"github.com/docker/distribution/digest"
|
||||
"github.com/docker/distribution/registry/api/errcode"
|
||||
"github.com/docker/distribution/registry/api/v2"
|
||||
"github.com/docker/distribution/registry/auth"
|
||||
"github.com/opencontainers/go-digest"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
@@ -62,7 +61,7 @@ func getDigest(ctx context.Context) (dgst digest.Digest, err error) {
|
||||
return "", errDigestNotAvailable
|
||||
}
|
||||
|
||||
d, err := digest.ParseDigest(dgstStr)
|
||||
d, err := digest.Parse(dgstStr)
|
||||
if err != nil {
|
||||
ctxu.GetLogger(ctx).Errorf("error parsing digest=%q: %v", dgstStr, err)
|
||||
return "", err
|
||||
@@ -91,62 +90,3 @@ func getUserName(ctx context.Context, r *http.Request) string {
|
||||
|
||||
return username
|
||||
}
|
||||
|
||||
// contextManager allows us to associate net/context.Context instances with a
|
||||
// request, based on the memory identity of http.Request. This prepares http-
|
||||
// level context, which is not application specific. If this is called,
|
||||
// (*contextManager).release must be called on the context when the request is
|
||||
// completed.
|
||||
//
|
||||
// Providing this circumvents a lot of necessity for dispatchers with the
|
||||
// benefit of instantiating the request context much earlier.
|
||||
//
|
||||
// TODO(stevvooe): Consider making this facility a part of the context package.
|
||||
type contextManager struct {
|
||||
contexts map[*http.Request]context.Context
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
// defaultContextManager is just a global instance to register request contexts.
|
||||
var defaultContextManager = newContextManager()
|
||||
|
||||
func newContextManager() *contextManager {
|
||||
return &contextManager{
|
||||
contexts: make(map[*http.Request]context.Context),
|
||||
}
|
||||
}
|
||||
|
||||
// context either returns a new context or looks it up in the manager.
|
||||
func (cm *contextManager) context(parent context.Context, w http.ResponseWriter, r *http.Request) context.Context {
|
||||
cm.mu.Lock()
|
||||
defer cm.mu.Unlock()
|
||||
|
||||
ctx, ok := cm.contexts[r]
|
||||
if ok {
|
||||
return ctx
|
||||
}
|
||||
|
||||
if parent == nil {
|
||||
parent = ctxu.Background()
|
||||
}
|
||||
|
||||
ctx = ctxu.WithRequest(parent, r)
|
||||
ctx, w = ctxu.WithResponseWriter(ctx, w)
|
||||
ctx = ctxu.WithLogger(ctx, ctxu.GetRequestLogger(ctx))
|
||||
cm.contexts[r] = ctx
|
||||
|
||||
return ctx
|
||||
}
|
||||
|
||||
// releases frees any associated with resources from request.
|
||||
func (cm *contextManager) release(ctx context.Context) {
|
||||
cm.mu.Lock()
|
||||
defer cm.mu.Unlock()
|
||||
|
||||
r, err := ctxu.GetRequest(ctx)
|
||||
if err != nil {
|
||||
ctxu.GetLogger(ctx).Errorf("no request found in context during release")
|
||||
return
|
||||
}
|
||||
delete(cm.contexts, r)
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ import (
|
||||
|
||||
"github.com/docker/distribution"
|
||||
ctxu "github.com/docker/distribution/context"
|
||||
"github.com/docker/distribution/digest"
|
||||
"github.com/docker/distribution/manifest/manifestlist"
|
||||
"github.com/docker/distribution/manifest/schema1"
|
||||
"github.com/docker/distribution/manifest/schema2"
|
||||
@@ -17,6 +16,7 @@ import (
|
||||
"github.com/docker/distribution/registry/api/v2"
|
||||
"github.com/docker/distribution/registry/auth"
|
||||
"github.com/gorilla/handlers"
|
||||
"github.com/opencontainers/go-digest"
|
||||
)
|
||||
|
||||
// These constants determine which architecture and OS to choose from a
|
||||
@@ -26,36 +26,36 @@ const (
|
||||
defaultOS = "linux"
|
||||
)
|
||||
|
||||
// imageManifestDispatcher takes the request context and builds the
|
||||
// appropriate handler for handling image manifest requests.
|
||||
func imageManifestDispatcher(ctx *Context, r *http.Request) http.Handler {
|
||||
imageManifestHandler := &imageManifestHandler{
|
||||
// manifestDispatcher takes the request context and builds the
|
||||
// appropriate handler for handling manifest requests.
|
||||
func manifestDispatcher(ctx *Context, r *http.Request) http.Handler {
|
||||
manifestHandler := &manifestHandler{
|
||||
Context: ctx,
|
||||
}
|
||||
reference := getReference(ctx)
|
||||
dgst, err := digest.ParseDigest(reference)
|
||||
dgst, err := digest.Parse(reference)
|
||||
if err != nil {
|
||||
// We just have a tag
|
||||
imageManifestHandler.Tag = reference
|
||||
manifestHandler.Tag = reference
|
||||
} else {
|
||||
imageManifestHandler.Digest = dgst
|
||||
manifestHandler.Digest = dgst
|
||||
}
|
||||
|
||||
mhandler := handlers.MethodHandler{
|
||||
"GET": http.HandlerFunc(imageManifestHandler.GetImageManifest),
|
||||
"HEAD": http.HandlerFunc(imageManifestHandler.GetImageManifest),
|
||||
"GET": http.HandlerFunc(manifestHandler.GetManifest),
|
||||
"HEAD": http.HandlerFunc(manifestHandler.GetManifest),
|
||||
}
|
||||
|
||||
if !ctx.readOnly {
|
||||
mhandler["PUT"] = http.HandlerFunc(imageManifestHandler.PutImageManifest)
|
||||
mhandler["DELETE"] = http.HandlerFunc(imageManifestHandler.DeleteImageManifest)
|
||||
mhandler["PUT"] = http.HandlerFunc(manifestHandler.PutManifest)
|
||||
mhandler["DELETE"] = http.HandlerFunc(manifestHandler.DeleteManifest)
|
||||
}
|
||||
|
||||
return mhandler
|
||||
}
|
||||
|
||||
// imageManifestHandler handles http operations on image manifests.
|
||||
type imageManifestHandler struct {
|
||||
// manifestHandler handles http operations on image manifests.
|
||||
type manifestHandler struct {
|
||||
*Context
|
||||
|
||||
// One of tag or digest gets set, depending on what is present in context.
|
||||
@@ -63,8 +63,8 @@ type imageManifestHandler struct {
|
||||
Digest digest.Digest
|
||||
}
|
||||
|
||||
// GetImageManifest fetches the image manifest from the storage backend, if it exists.
|
||||
func (imh *imageManifestHandler) GetImageManifest(w http.ResponseWriter, r *http.Request) {
|
||||
// GetManifest fetches the image manifest from the storage backend, if it exists.
|
||||
func (imh *manifestHandler) GetManifest(w http.ResponseWriter, r *http.Request) {
|
||||
ctxu.GetLogger(imh).Debug("GetImageManifest")
|
||||
manifests, err := imh.Repository.Manifests(imh)
|
||||
if err != nil {
|
||||
@@ -77,7 +77,11 @@ func (imh *imageManifestHandler) GetImageManifest(w http.ResponseWriter, r *http
|
||||
tags := imh.Repository.Tags(imh)
|
||||
desc, err := tags.Get(imh, imh.Tag)
|
||||
if err != nil {
|
||||
imh.Errors = append(imh.Errors, v2.ErrorCodeManifestUnknown.WithDetail(err))
|
||||
if _, ok := err.(distribution.ErrTagUnknown); ok {
|
||||
imh.Errors = append(imh.Errors, v2.ErrorCodeManifestUnknown.WithDetail(err))
|
||||
} else {
|
||||
imh.Errors = append(imh.Errors, errcode.ErrorCodeUnknown.WithDetail(err))
|
||||
}
|
||||
return
|
||||
}
|
||||
imh.Digest = desc.Digest
|
||||
@@ -94,7 +98,11 @@ func (imh *imageManifestHandler) GetImageManifest(w http.ResponseWriter, r *http
|
||||
}
|
||||
manifest, err = manifests.Get(imh, imh.Digest, options...)
|
||||
if err != nil {
|
||||
imh.Errors = append(imh.Errors, v2.ErrorCodeManifestUnknown.WithDetail(err))
|
||||
if _, ok := err.(distribution.ErrManifestUnknownRevision); ok {
|
||||
imh.Errors = append(imh.Errors, v2.ErrorCodeManifestUnknown.WithDetail(err))
|
||||
} else {
|
||||
imh.Errors = append(imh.Errors, errcode.ErrorCodeUnknown.WithDetail(err))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -161,7 +169,11 @@ func (imh *imageManifestHandler) GetImageManifest(w http.ResponseWriter, r *http
|
||||
|
||||
manifest, err = manifests.Get(imh, manifestDigest)
|
||||
if err != nil {
|
||||
imh.Errors = append(imh.Errors, v2.ErrorCodeManifestUnknown.WithDetail(err))
|
||||
if _, ok := err.(distribution.ErrManifestUnknownRevision); ok {
|
||||
imh.Errors = append(imh.Errors, v2.ErrorCodeManifestUnknown.WithDetail(err))
|
||||
} else {
|
||||
imh.Errors = append(imh.Errors, errcode.ErrorCodeUnknown.WithDetail(err))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -171,6 +183,8 @@ func (imh *imageManifestHandler) GetImageManifest(w http.ResponseWriter, r *http
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
imh.Digest = manifestDigest
|
||||
}
|
||||
}
|
||||
|
||||
@@ -186,12 +200,16 @@ func (imh *imageManifestHandler) GetImageManifest(w http.ResponseWriter, r *http
|
||||
w.Write(p)
|
||||
}
|
||||
|
||||
func (imh *imageManifestHandler) convertSchema2Manifest(schema2Manifest *schema2.DeserializedManifest) (distribution.Manifest, error) {
|
||||
func (imh *manifestHandler) convertSchema2Manifest(schema2Manifest *schema2.DeserializedManifest) (distribution.Manifest, error) {
|
||||
targetDescriptor := schema2Manifest.Target()
|
||||
blobs := imh.Repository.Blobs(imh)
|
||||
configJSON, err := blobs.Get(imh, targetDescriptor.Digest)
|
||||
if err != nil {
|
||||
imh.Errors = append(imh.Errors, v2.ErrorCodeManifestInvalid.WithDetail(err))
|
||||
if err == distribution.ErrBlobUnknown {
|
||||
imh.Errors = append(imh.Errors, v2.ErrorCodeManifestInvalid.WithDetail(err))
|
||||
} else {
|
||||
imh.Errors = append(imh.Errors, errcode.ErrorCodeUnknown.WithDetail(err))
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -231,8 +249,8 @@ func etagMatch(r *http.Request, etag string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// PutImageManifest validates and stores an image in the registry.
|
||||
func (imh *imageManifestHandler) PutImageManifest(w http.ResponseWriter, r *http.Request) {
|
||||
// PutManifest validates and stores a manifest in the registry.
|
||||
func (imh *manifestHandler) PutManifest(w http.ResponseWriter, r *http.Request) {
|
||||
ctxu.GetLogger(imh).Debug("PutImageManifest")
|
||||
manifests, err := imh.Repository.Manifests(imh)
|
||||
if err != nil {
|
||||
@@ -348,7 +366,7 @@ func (imh *imageManifestHandler) PutImageManifest(w http.ResponseWriter, r *http
|
||||
|
||||
// applyResourcePolicy checks whether the resource class matches what has
|
||||
// been authorized and allowed by the policy configuration.
|
||||
func (imh *imageManifestHandler) applyResourcePolicy(manifest distribution.Manifest) error {
|
||||
func (imh *manifestHandler) applyResourcePolicy(manifest distribution.Manifest) error {
|
||||
allowedClasses := imh.App.Config.Policy.Repository.Classes
|
||||
if len(allowedClasses) == 0 {
|
||||
return nil
|
||||
@@ -360,7 +378,7 @@ func (imh *imageManifestHandler) applyResourcePolicy(manifest distribution.Manif
|
||||
class = "image"
|
||||
case *schema2.DeserializedManifest:
|
||||
switch m.Config.MediaType {
|
||||
case schema2.MediaTypeConfig:
|
||||
case schema2.MediaTypeImageConfig:
|
||||
class = "image"
|
||||
case schema2.MediaTypePluginConfig:
|
||||
class = "plugin"
|
||||
@@ -413,8 +431,8 @@ func (imh *imageManifestHandler) applyResourcePolicy(manifest distribution.Manif
|
||||
|
||||
}
|
||||
|
||||
// DeleteImageManifest removes the manifest with the given digest from the registry.
|
||||
func (imh *imageManifestHandler) DeleteImageManifest(w http.ResponseWriter, r *http.Request) {
|
||||
// DeleteManifest removes the manifest with the given digest from the registry.
|
||||
func (imh *manifestHandler) DeleteManifest(w http.ResponseWriter, r *http.Request) {
|
||||
ctxu.GetLogger(imh).Debug("DeleteImageManifest")
|
||||
|
||||
manifests, err := imh.Repository.Manifests(imh)
|
||||
2
vendor/github.com/docker/distribution/registry/proxy/proxyblobstore.go
generated
vendored
2
vendor/github.com/docker/distribution/registry/proxy/proxyblobstore.go
generated
vendored
@@ -9,9 +9,9 @@ import (
|
||||
|
||||
"github.com/docker/distribution"
|
||||
"github.com/docker/distribution/context"
|
||||
"github.com/docker/distribution/digest"
|
||||
"github.com/docker/distribution/reference"
|
||||
"github.com/docker/distribution/registry/proxy/scheduler"
|
||||
"github.com/opencontainers/go-digest"
|
||||
)
|
||||
|
||||
// todo(richardscothern): from cache control header or config file
|
||||
|
||||
4
vendor/github.com/docker/distribution/registry/proxy/proxyblobstore_test.go
generated
vendored
4
vendor/github.com/docker/distribution/registry/proxy/proxyblobstore_test.go
generated
vendored
@@ -11,13 +11,13 @@ import (
|
||||
|
||||
"github.com/docker/distribution"
|
||||
"github.com/docker/distribution/context"
|
||||
"github.com/docker/distribution/digest"
|
||||
"github.com/docker/distribution/reference"
|
||||
"github.com/docker/distribution/registry/proxy/scheduler"
|
||||
"github.com/docker/distribution/registry/storage"
|
||||
"github.com/docker/distribution/registry/storage/cache/memory"
|
||||
"github.com/docker/distribution/registry/storage/driver/filesystem"
|
||||
"github.com/docker/distribution/registry/storage/driver/inmemory"
|
||||
"github.com/opencontainers/go-digest"
|
||||
)
|
||||
|
||||
var sbsMu sync.Mutex
|
||||
@@ -115,7 +115,7 @@ func (te *testEnv) RemoteStats() *map[string]int {
|
||||
|
||||
// Populate remote store and record the digests
|
||||
func makeTestEnv(t *testing.T, name string) *testEnv {
|
||||
nameRef, err := reference.ParseNamed(name)
|
||||
nameRef, err := reference.WithName(name)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to parse reference: %s", err)
|
||||
}
|
||||
|
||||
2
vendor/github.com/docker/distribution/registry/proxy/proxymanifeststore.go
generated
vendored
2
vendor/github.com/docker/distribution/registry/proxy/proxymanifeststore.go
generated
vendored
@@ -5,9 +5,9 @@ import (
|
||||
|
||||
"github.com/docker/distribution"
|
||||
"github.com/docker/distribution/context"
|
||||
"github.com/docker/distribution/digest"
|
||||
"github.com/docker/distribution/reference"
|
||||
"github.com/docker/distribution/registry/proxy/scheduler"
|
||||
"github.com/opencontainers/go-digest"
|
||||
)
|
||||
|
||||
// todo(richardscothern): from cache control header or config
|
||||
|
||||
4
vendor/github.com/docker/distribution/registry/proxy/proxymanifeststore_test.go
generated
vendored
4
vendor/github.com/docker/distribution/registry/proxy/proxymanifeststore_test.go
generated
vendored
@@ -7,7 +7,6 @@ import (
|
||||
|
||||
"github.com/docker/distribution"
|
||||
"github.com/docker/distribution/context"
|
||||
"github.com/docker/distribution/digest"
|
||||
"github.com/docker/distribution/manifest"
|
||||
"github.com/docker/distribution/manifest/schema1"
|
||||
"github.com/docker/distribution/reference"
|
||||
@@ -19,6 +18,7 @@ import (
|
||||
"github.com/docker/distribution/registry/storage/driver/inmemory"
|
||||
"github.com/docker/distribution/testutil"
|
||||
"github.com/docker/libtrust"
|
||||
"github.com/opencontainers/go-digest"
|
||||
)
|
||||
|
||||
type statsManifest struct {
|
||||
@@ -83,7 +83,7 @@ func (m *mockChallenger) challengeManager() challenge.Manager {
|
||||
}
|
||||
|
||||
func newManifestStoreTestEnv(t *testing.T, name, tag string) *manifestStoreTestEnv {
|
||||
nameRef, err := reference.ParseNamed(name)
|
||||
nameRef, err := reference.WithName(name)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to parse reference: %s", err)
|
||||
}
|
||||
|
||||
2
vendor/github.com/docker/distribution/registry/registry.go
generated
vendored
2
vendor/github.com/docker/distribution/registry/registry.go
generated
vendored
@@ -12,7 +12,7 @@ import (
|
||||
"rsc.io/letsencrypt"
|
||||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"github.com/Sirupsen/logrus/formatters/logstash"
|
||||
logstash "github.com/bshuster-repo/logrus-logstash-hook"
|
||||
"github.com/bugsnag/bugsnag-go"
|
||||
"github.com/docker/distribution/configuration"
|
||||
"github.com/docker/distribution/context"
|
||||
|
||||
16
vendor/github.com/docker/distribution/registry/storage/blob_test.go
generated
vendored
16
vendor/github.com/docker/distribution/registry/storage/blob_test.go
generated
vendored
@@ -13,18 +13,18 @@ import (
|
||||
|
||||
"github.com/docker/distribution"
|
||||
"github.com/docker/distribution/context"
|
||||
"github.com/docker/distribution/digest"
|
||||
"github.com/docker/distribution/reference"
|
||||
"github.com/docker/distribution/registry/storage/cache/memory"
|
||||
"github.com/docker/distribution/registry/storage/driver/testdriver"
|
||||
"github.com/docker/distribution/testutil"
|
||||
"github.com/opencontainers/go-digest"
|
||||
)
|
||||
|
||||
// TestWriteSeek tests that the current file size can be
|
||||
// obtained using Seek
|
||||
func TestWriteSeek(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
imageName, _ := reference.ParseNamed("foo/bar")
|
||||
imageName, _ := reference.WithName("foo/bar")
|
||||
driver := testdriver.New()
|
||||
registry, err := NewRegistry(ctx, driver, BlobDescriptorCacheProvider(memory.NewInMemoryBlobDescriptorCacheProvider()), EnableDelete, EnableRedirect)
|
||||
if err != nil {
|
||||
@@ -60,7 +60,7 @@ func TestSimpleBlobUpload(t *testing.T) {
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
imageName, _ := reference.ParseNamed("foo/bar")
|
||||
imageName, _ := reference.WithName("foo/bar")
|
||||
driver := testdriver.New()
|
||||
registry, err := NewRegistry(ctx, driver, BlobDescriptorCacheProvider(memory.NewInMemoryBlobDescriptorCacheProvider()), EnableDelete, EnableRedirect)
|
||||
if err != nil {
|
||||
@@ -255,7 +255,7 @@ func TestSimpleBlobUpload(t *testing.T) {
|
||||
// other tests.
|
||||
func TestSimpleBlobRead(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
imageName, _ := reference.ParseNamed("foo/bar")
|
||||
imageName, _ := reference.WithName("foo/bar")
|
||||
driver := testdriver.New()
|
||||
registry, err := NewRegistry(ctx, driver, BlobDescriptorCacheProvider(memory.NewInMemoryBlobDescriptorCacheProvider()), EnableDelete, EnableRedirect)
|
||||
if err != nil {
|
||||
@@ -366,8 +366,8 @@ func TestBlobMount(t *testing.T) {
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
imageName, _ := reference.ParseNamed("foo/bar")
|
||||
sourceImageName, _ := reference.ParseNamed("foo/source")
|
||||
imageName, _ := reference.WithName("foo/bar")
|
||||
sourceImageName, _ := reference.WithName("foo/source")
|
||||
driver := testdriver.New()
|
||||
registry, err := NewRegistry(ctx, driver, BlobDescriptorCacheProvider(memory.NewInMemoryBlobDescriptorCacheProvider()), EnableDelete, EnableRedirect)
|
||||
if err != nil {
|
||||
@@ -518,7 +518,7 @@ func TestBlobMount(t *testing.T) {
|
||||
// TestLayerUploadZeroLength uploads zero-length
|
||||
func TestLayerUploadZeroLength(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
imageName, _ := reference.ParseNamed("foo/bar")
|
||||
imageName, _ := reference.WithName("foo/bar")
|
||||
driver := testdriver.New()
|
||||
registry, err := NewRegistry(ctx, driver, BlobDescriptorCacheProvider(memory.NewInMemoryBlobDescriptorCacheProvider()), EnableDelete, EnableRedirect)
|
||||
if err != nil {
|
||||
@@ -530,7 +530,7 @@ func TestLayerUploadZeroLength(t *testing.T) {
|
||||
}
|
||||
bs := repository.Blobs(ctx)
|
||||
|
||||
simpleUpload(t, bs, []byte{}, digest.DigestSha256EmptyTar)
|
||||
simpleUpload(t, bs, []byte{}, digestSha256Empty)
|
||||
}
|
||||
|
||||
func simpleUpload(t *testing.T, bs distribution.BlobIngester, blob []byte, expectedDigest digest.Digest) {
|
||||
|
||||
2
vendor/github.com/docker/distribution/registry/storage/blobserver.go
generated
vendored
2
vendor/github.com/docker/distribution/registry/storage/blobserver.go
generated
vendored
@@ -7,8 +7,8 @@ import (
|
||||
|
||||
"github.com/docker/distribution"
|
||||
"github.com/docker/distribution/context"
|
||||
"github.com/docker/distribution/digest"
|
||||
"github.com/docker/distribution/registry/storage/driver"
|
||||
"github.com/opencontainers/go-digest"
|
||||
)
|
||||
|
||||
// TODO(stevvooe): This should configurable in the future.
|
||||
|
||||
4
vendor/github.com/docker/distribution/registry/storage/blobstore.go
generated
vendored
4
vendor/github.com/docker/distribution/registry/storage/blobstore.go
generated
vendored
@@ -5,8 +5,8 @@ import (
|
||||
|
||||
"github.com/docker/distribution"
|
||||
"github.com/docker/distribution/context"
|
||||
"github.com/docker/distribution/digest"
|
||||
"github.com/docker/distribution/registry/storage/driver"
|
||||
"github.com/opencontainers/go-digest"
|
||||
)
|
||||
|
||||
// blobStore implements the read side of the blob store interface over a
|
||||
@@ -145,7 +145,7 @@ func (bs *blobStore) readlink(ctx context.Context, path string) (digest.Digest,
|
||||
return "", err
|
||||
}
|
||||
|
||||
linked, err := digest.ParseDigest(string(content))
|
||||
linked, err := digest.Parse(string(content))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
25
vendor/github.com/docker/distribution/registry/storage/blobwriter.go
generated
vendored
25
vendor/github.com/docker/distribution/registry/storage/blobwriter.go
generated
vendored
@@ -10,14 +10,19 @@ import (
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/docker/distribution"
|
||||
"github.com/docker/distribution/context"
|
||||
"github.com/docker/distribution/digest"
|
||||
storagedriver "github.com/docker/distribution/registry/storage/driver"
|
||||
"github.com/opencontainers/go-digest"
|
||||
)
|
||||
|
||||
var (
|
||||
errResumableDigestNotAvailable = errors.New("resumable digest not available")
|
||||
)
|
||||
|
||||
const (
|
||||
// digestSha256Empty is the canonical sha256 digest of empty data
|
||||
digestSha256Empty = "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
|
||||
)
|
||||
|
||||
// blobWriter is used to control the various aspects of resumable
|
||||
// blob upload.
|
||||
type blobWriter struct {
|
||||
@@ -234,12 +239,8 @@ func (bw *blobWriter) validateBlob(ctx context.Context, desc distribution.Descri
|
||||
// paths. We may be able to make the size-based check a stronger
|
||||
// guarantee, so this may be defensive.
|
||||
if !verified {
|
||||
digester := digest.Canonical.New()
|
||||
|
||||
digestVerifier, err := digest.NewDigestVerifier(desc.Digest)
|
||||
if err != nil {
|
||||
return distribution.Descriptor{}, err
|
||||
}
|
||||
digester := digest.Canonical.Digester()
|
||||
verifier := desc.Digest.Verifier()
|
||||
|
||||
// Read the file from the backend driver and validate it.
|
||||
fr, err := newFileReader(ctx, bw.driver, bw.path, desc.Size)
|
||||
@@ -250,12 +251,12 @@ func (bw *blobWriter) validateBlob(ctx context.Context, desc distribution.Descri
|
||||
|
||||
tr := io.TeeReader(fr, digester.Hash())
|
||||
|
||||
if _, err := io.Copy(digestVerifier, tr); err != nil {
|
||||
if _, err := io.Copy(verifier, tr); err != nil {
|
||||
return distribution.Descriptor{}, err
|
||||
}
|
||||
|
||||
canonical = digester.Digest()
|
||||
verified = digestVerifier.Verified()
|
||||
verified = verifier.Verified()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -313,7 +314,7 @@ func (bw *blobWriter) moveBlob(ctx context.Context, desc distribution.Descriptor
|
||||
// If no data was received, we may not actually have a file on disk. Check
|
||||
// the size here and write a zero-length file to blobPath if this is the
|
||||
// case. For the most part, this should only ever happen with zero-length
|
||||
// tars.
|
||||
// blobs.
|
||||
if _, err := bw.blobStore.driver.Stat(ctx, bw.path); err != nil {
|
||||
switch err := err.(type) {
|
||||
case storagedriver.PathNotFoundError:
|
||||
@@ -321,8 +322,8 @@ func (bw *blobWriter) moveBlob(ctx context.Context, desc distribution.Descriptor
|
||||
// get a hash, then the underlying file is deleted, we risk moving
|
||||
// a zero-length blob into a nonzero-length blob location. To
|
||||
// prevent this horrid thing, we employ the hack of only allowing
|
||||
// to this happen for the digest of an empty tar.
|
||||
if desc.Digest == digest.DigestSha256EmptyTar {
|
||||
// to this happen for the digest of an empty blob.
|
||||
if desc.Digest == digestSha256Empty {
|
||||
return bw.blobStore.driver.PutContent(ctx, blobPath, []byte{})
|
||||
}
|
||||
|
||||
|
||||
2
vendor/github.com/docker/distribution/registry/storage/cache/cachecheck/suite.go
generated
vendored
2
vendor/github.com/docker/distribution/registry/storage/cache/cachecheck/suite.go
generated
vendored
@@ -6,8 +6,8 @@ import (
|
||||
|
||||
"github.com/docker/distribution"
|
||||
"github.com/docker/distribution/context"
|
||||
"github.com/docker/distribution/digest"
|
||||
"github.com/docker/distribution/registry/storage/cache"
|
||||
"github.com/opencontainers/go-digest"
|
||||
)
|
||||
|
||||
// CheckBlobDescriptorCache takes a cache implementation through a common set
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user