Browse code

Vendor distribution to correct config blob media type in schema2 manifest

@nwt noticed that the media type specified in the config section of a
schema2 manifest is application/octet-stream, instead of the correct
value application/vnd.docker.container.image.v1+json.

This brings in https://github.com/docker/distribution/pull/1622 to fix
this.

Signed-off-by: Aaron Lehmann <aaron.lehmann@docker.com>

Aaron Lehmann authored on 2016/04/12 09:29:17
Showing 17 changed files
... ...
@@ -17,7 +17,6 @@ import (
17 17
 	"github.com/docker/distribution/manifest/schema1"
18 18
 	"github.com/docker/distribution/manifest/schema2"
19 19
 	"github.com/docker/distribution/registry/api/errcode"
20
-	"github.com/docker/distribution/registry/client"
21 20
 	"github.com/docker/distribution/registry/client/auth"
22 21
 	"github.com/docker/distribution/registry/client/transport"
23 22
 	"github.com/docker/docker/distribution/metadata"
... ...
@@ -338,7 +337,7 @@ func (p *v2Puller) pullV2Tag(ctx context.Context, ref reference.Named) (tagUpdat
338 338
 		// NOTE: not using TagService.Get, since it uses HEAD requests
339 339
 		// against the manifests endpoint, which are not supported by
340 340
 		// all registry versions.
341
-		manifest, err = manSvc.Get(ctx, "", client.WithTag(tagged.Tag()))
341
+		manifest, err = manSvc.Get(ctx, "", distribution.WithTag(tagged.Tag()))
342 342
 		if err != nil {
343 343
 			return false, allowV1Fallback(err)
344 344
 		}
... ...
@@ -166,7 +166,7 @@ func (p *v2Pusher) pushV2Tag(ctx context.Context, ref reference.NamedTagged, ima
166 166
 		return err
167 167
 	}
168 168
 
169
-	putOptions := []distribution.ManifestServiceOption{client.WithTag(ref.Tag())}
169
+	putOptions := []distribution.ManifestServiceOption{distribution.WithTag(ref.Tag())}
170 170
 	if _, err = manSvc.Put(ctx, manifest, putOptions...); err != nil {
171 171
 		logrus.Warnf("failed to upload schema2 manifest: %v - falling back to schema1", err)
172 172
 
... ...
@@ -49,7 +49,7 @@ clone git github.com/boltdb/bolt v1.2.0
49 49
 clone git github.com/miekg/dns 75e6e86cc601825c5dbcd4e0c209eab180997cd7
50 50
 
51 51
 # get graph and distribution packages
52
-clone git github.com/docker/distribution d06d6d3b093302c02a93153ac7b06ebc0ffd1793
52
+clone git github.com/docker/distribution 467fc068d88aa6610691b7f1a677271a3fac4aac
53 53
 clone git github.com/vbatts/tar-split v0.9.11
54 54
 
55 55
 # get desired notary commit, might also need to be updated in Dockerfile
56 56
deleted file mode 100644
... ...
@@ -1,38 +0,0 @@
1
-image: dmp42/go:stable
2
-
3
-script:
4
-  # To be spoofed back into the test image
5
-  - go get github.com/modocache/gover
6
-
7
-  - go get -t ./...
8
-
9
-  # Go fmt
10
-  - test -z "$(gofmt -s -l -w .     | tee /dev/stderr)"
11
-  # Go lint
12
-  - test -z "$(golint ./...          | tee /dev/stderr)"
13
-  # Go vet
14
-  - go vet ./...
15
-  # Go test
16
-  - go test -v -race -cover ./...
17
-  # Helper to concatenate reports
18
-  - gover
19
-  # Send to coverall
20
-  - goveralls -service drone.io -coverprofile=gover.coverprofile -repotoken {{COVERALLS_TOKEN}}
21
-
22
-  # Do we want these as well?
23
-  # - go get code.google.com/p/go.tools/cmd/goimports
24
-  # - test -z "$(goimports -l -w ./... | tee /dev/stderr)"
25
-  # http://labix.org/gocheck
26
-
27
-notify:
28
-    email:
29
-        recipients:
30
-            - distribution@docker.com
31
-
32
-    slack:
33
-        team: docker
34
-        channel: "#dt"
35
-        username: mom
36
-        token: {{SLACK_TOKEN}}
37
-        on_success: true
38
-        on_failure: true
... ...
@@ -13,3 +13,4 @@ Sharif Nassar <sharif@mrwacky.com> Sharif Nassar <mrwacky42@users.noreply.github
13 13
 Sven Dowideit <SvenDowideit@home.org.au> Sven Dowideit <SvenDowideit@users.noreply.github.com>
14 14
 Vincent Giersch <vincent.giersch@ovh.net> Vincent Giersch <vincent@giersch.fr>
15 15
 davidli <wenquan.li@hp.com> davidli <wenquan.li@hpe.com>
16
+Omer Cohen <git@omer.io> Omer Cohen <git@omerc.net>
16 17
\ No newline at end of file
... ...
@@ -8,12 +8,15 @@ Alex Elman <aelman@indeed.com>
8 8
 amitshukla <ashukla73@hotmail.com>
9 9
 Amy Lindburg <amy.lindburg@docker.com>
10 10
 Andrew Meredith <andymeredith@gmail.com>
11
+Andrew T Nguyen <andrew.nguyen@docker.com>
11 12
 Andrey Kostov <kostov.andrey@gmail.com>
12 13
 Andy Goldstein <agoldste@redhat.com>
13 14
 Anton Tiurin <noxiouz@yandex.ru>
14 15
 Antonio Mercado <amercado@thinknode.com>
16
+Antonio Murdaca <runcom@redhat.com>
15 17
 Arnaud Porterie <arnaud.porterie@docker.com>
16 18
 Arthur Baars <arthur@semmle.com>
19
+Asuka Suzuki <hello@tanksuzuki.com>
17 20
 Avi Miller <avi.miller@oracle.com>
18 21
 Ayose Cazorla <ayosec@gmail.com>
19 22
 BadZen <dave.trombley@gmail.com>
... ...
@@ -37,7 +40,9 @@ Diogo Mónica <diogo.monica@gmail.com>
37 37
 DJ Enriquez <dj.enriquez@infospace.com>
38 38
 Donald Huang <don.hcd@gmail.com>
39 39
 Doug Davis <dug@us.ibm.com>
40
+Eric Yang <windfarer@gmail.com>
40 41
 farmerworking <farmerworking@gmail.com>
42
+Felix Yan <felixonmars@archlinux.org>
41 43
 Florentin Raud <florentin.raud@gmail.com>
42 44
 Frederick F. Kautz IV <fkautz@alumni.cmu.edu>
43 45
 gabriell nascimento <gabriell@bluesoft.com.br>
... ...
@@ -45,29 +50,36 @@ harche <p.harshal@gmail.com>
45 45
 Henri Gomez <henri.gomez@gmail.com>
46 46
 Hu Keping <hukeping@huawei.com>
47 47
 Hua Wang <wanghua.humble@gmail.com>
48
+HuKeping <hukeping@huawei.com>
48 49
 Ian Babrou <ibobrik@gmail.com>
50
+igayoso <igayoso@gmail.com>
49 51
 Jack Griffin <jackpg14@gmail.com>
50 52
 Jason Freidman <jason.freidman@gmail.com>
51 53
 Jeff Nickoloff <jeff@allingeek.com>
52 54
 Jessie Frazelle <jessie@docker.com>
53 55
 Jianqing Wang <tsing@jianqing.org>
56
+John Starks <jostarks@microsoft.com>
54 57
 Jon Poler <jonathan.poler@apcera.com>
55 58
 Jonathan Boulle <jonathanboulle@gmail.com>
56 59
 Jordan Liggitt <jliggitt@redhat.com>
57 60
 Josh Hawn <josh.hawn@docker.com>
58 61
 Julien Fernandez <julien.fernandez@gmail.com>
62
+Keerthan Mala <kmala@engineyard.com>
59 63
 Kelsey Hightower <kelsey.hightower@gmail.com>
60 64
 Kenneth Lim <kennethlimcp@gmail.com>
61 65
 Kenny Leung <kleung@google.com>
62 66
 Li Yi <denverdino@gmail.com>
63 67
 Liu Hua <sdu.liu@huawei.com>
68
+liuchang0812 <liuchang0812@gmail.com>
64 69
 Louis Kottmann <louis.kottmann@gmail.com>
65 70
 Luke Carpenter <x@rubynerd.net>
66 71
 Mary Anthony <mary@docker.com>
67 72
 Matt Bentley <mbentley@mbentley.net>
73
+Matt Duch <matt@learnmetrics.com>
68 74
 Matt Moore <mattmoor@google.com>
69 75
 Matt Robenolt <matt@ydekproductions.com>
70 76
 Michael Prokop <mika@grml.org>
77
+Michal Minar <miminar@redhat.com>
71 78
 Miquel Sabaté <msabate@suse.com>
72 79
 Morgan Bauer <mbauer@us.ibm.com>
73 80
 moxiegirl <mary@docker.com>
... ...
@@ -78,6 +90,7 @@ Nuutti Kotivuori <nuutti.kotivuori@poplatek.fi>
78 78
 Oilbeater <liumengxinfly@gmail.com>
79 79
 Olivier Gambier <olivier@docker.com>
80 80
 Olivier Jacques <olivier.jacques@hp.com>
81
+Omer Cohen <git@omer.io>
81 82
 Patrick Devine <patrick.devine@docker.com>
82 83
 Philip Misiowiec <philip@atlashealth.com>
83 84
 Richard Scothern <richard.scothern@docker.com>
... ...
@@ -90,6 +103,7 @@ Shawn Falkner-Horine <dreadpirateshawn@gmail.com>
90 90
 Shreyas Karnik <karnik.shreyas@gmail.com>
91 91
 Simon Thulbourn <simon+github@thulbourn.com>
92 92
 Spencer Rinehart <anubis@overthemonkey.com>
93
+Stefan Weil <sw@weilnetz.de>
93 94
 Stephen J Day <stephen.day@docker.com>
94 95
 Sungho Moon <sungho.moon@navercorp.com>
95 96
 Sven Dowideit <SvenDowideit@home.org.au>
... ...
@@ -111,3 +125,4 @@ xg.song <xg.song@venusource.com>
111 111
 xiekeyang <xiekeyang@huawei.com>
112 112
 Yann ROBERT <yann.robert@anantaplex.fr>
113 113
 yuzou <zouyu7@huawei.com>
114
+姜继忠 <jizhong.jiangjz@alibaba-inc.com>
... ...
@@ -1,11 +1,10 @@
1
-FROM golang:1.5.3
1
+FROM golang:1.6
2 2
 
3 3
 RUN apt-get update && \
4 4
     apt-get install -y apache2-utils && \
5 5
     rm -rf /var/lib/apt/lists/*
6 6
 
7 7
 ENV DISTRIBUTION_DIR /go/src/github.com/docker/distribution
8
-ENV GOPATH $DISTRIBUTION_DIR/Godeps/_workspace:$GOPATH
9 8
 ENV DOCKER_BUILDTAGS include_oss include_gcs
10 9
 
11 10
 WORKDIR $DISTRIBUTION_DIR
... ...
@@ -15,7 +15,7 @@ GO_LDFLAGS=-ldflags "-X `go list ./version`.Version=$(VERSION)"
15 15
 
16 16
 .PHONY: clean all fmt vet lint build test binaries
17 17
 .DEFAULT: all
18
-all: fmt vet fmt lint build test binaries
18
+all: fmt vet lint build test binaries
19 19
 
20 20
 AUTHORS: .mailmap .git/HEAD
21 21
 	 git log --format='%aN <%aE>' | sort -fu > $@
... ...
@@ -24,51 +24,83 @@ AUTHORS: .mailmap .git/HEAD
24 24
 version/version.go:
25 25
 	./version/version.sh > $@
26 26
 
27
-${PREFIX}/bin/registry: version/version.go $(shell find . -type f -name '*.go')
27
+# Required for go 1.5 to build
28
+GO15VENDOREXPERIMENT := 1
29
+
30
+# Package list
31
+PKGS := $(shell go list -tags "${DOCKER_BUILDTAGS}" ./... | grep -v ^github.com/docker/distribution/vendor/)
32
+
33
+# Resolving binary dependencies for specific targets
34
+GOLINT := $(shell which golint || echo '')
35
+GODEP := $(shell which godep || echo '')
36
+
37
+${PREFIX}/bin/registry: $(wildcard **/*.go)
28 38
 	@echo "+ $@"
29 39
 	@go build -tags "${DOCKER_BUILDTAGS}" -o $@ ${GO_LDFLAGS}  ${GO_GCFLAGS} ./cmd/registry
30 40
 
31
-${PREFIX}/bin/digest: version/version.go $(shell find . -type f -name '*.go')
41
+${PREFIX}/bin/digest:  $(wildcard **/*.go)
32 42
 	@echo "+ $@"
33 43
 	@go build -tags "${DOCKER_BUILDTAGS}" -o $@ ${GO_LDFLAGS}  ${GO_GCFLAGS} ./cmd/digest
34 44
 
35
-${PREFIX}/bin/registry-api-descriptor-template: version/version.go $(shell find . -type f -name '*.go')
45
+${PREFIX}/bin/registry-api-descriptor-template: $(wildcard **/*.go)
36 46
 	@echo "+ $@"
37 47
 	@go build -o $@ ${GO_LDFLAGS} ${GO_GCFLAGS} ./cmd/registry-api-descriptor-template
38 48
 
39 49
 docs/spec/api.md: docs/spec/api.md.tmpl ${PREFIX}/bin/registry-api-descriptor-template
40 50
 	./bin/registry-api-descriptor-template $< > $@
41 51
 
42
-# Depends on binaries because vet will silently fail if it can't load compiled
43
-# imports
44
-vet: binaries
52
+vet:
45 53
 	@echo "+ $@"
46
-	@go vet ./...
54
+	@go vet -tags "${DOCKER_BUILDTAGS}" $(PKGS)
47 55
 
48 56
 fmt:
49 57
 	@echo "+ $@"
50
-	@test -z "$$(gofmt -s -l . | grep -v Godeps/_workspace/src/ | tee /dev/stderr)" || \
51
-		echo "+ please format Go code with 'gofmt -s'"
58
+	@test -z "$$(gofmt -s -l . 2>&1 | grep -v ^vendor/ | tee /dev/stderr)" || \
59
+		(echo >&2 "+ please format Go code with 'gofmt -s'" && false)
52 60
 
53 61
 lint:
54 62
 	@echo "+ $@"
55
-	@test -z "$$(golint ./... | grep -v Godeps/_workspace/src/ | tee /dev/stderr)"
63
+	$(if $(GOLINT), , \
64
+		$(error Please install golint: `go get -u github.com/golang/lint/golint`))
65
+	@test -z "$$($(GOLINT) ./... 2>&1 | grep -v ^vendor/ | tee /dev/stderr)"
56 66
 
57 67
 build:
58 68
 	@echo "+ $@"
59
-	@go build -tags "${DOCKER_BUILDTAGS}" -v ${GO_LDFLAGS} ./...
69
+	@go build -tags "${DOCKER_BUILDTAGS}" -v ${GO_LDFLAGS} $(PKGS)
60 70
 
61 71
 test:
62 72
 	@echo "+ $@"
63
-	@go test -test.short -tags "${DOCKER_BUILDTAGS}" ./...
73
+	@go test -test.short -tags "${DOCKER_BUILDTAGS}" $(PKGS)
64 74
 
65 75
 test-full:
66 76
 	@echo "+ $@"
67
-	@go test ./...
77
+	@go test -tags "${DOCKER_BUILDTAGS}" $(PKGS)
68 78
 
69 79
 binaries: ${PREFIX}/bin/registry ${PREFIX}/bin/digest ${PREFIX}/bin/registry-api-descriptor-template
70 80
 	@echo "+ $@"
71 81
 
72 82
 clean:
73 83
 	@echo "+ $@"
74
-	@rm -rf "${PREFIX}/bin/registry" "${PREFIX}/bin/registry-api-descriptor-template"
84
+	@rm -rf "${PREFIX}/bin/registry" "${PREFIX}/bin/digest" "${PREFIX}/bin/registry-api-descriptor-template"
85
+
86
+dep-save:
87
+	@echo "+ $@"
88
+	$(if $(GODEP), , \
89
+		$(error Please install godep: go get github.com/tools/godep))
90
+	@$(GODEP) save $(PKGS)
91
+
92
+dep-restore:
93
+	@echo "+ $@"
94
+	$(if $(GODEP), , \
95
+		$(error Please install godep: go get github.com/tools/godep))
96
+	@$(GODEP) restore -v
97
+
98
+dep-validate: dep-restore
99
+	@echo "+ $@"
100
+	@rm -Rf .vendor.bak
101
+	@mv vendor .vendor.bak
102
+	@rm -Rf Godeps
103
+	@$(GODEP) save ./...
104
+	@test -z "$$(diff -r vendor .vendor.bak 2>&1 | tee /dev/stderr)" || \
105
+		(echo >&2 "+ borked dependencies! what you have in Godeps/Godeps.json does not match with what you have in vendor" && false)
106
+	@rm -Rf .vendor.bak
... ...
@@ -17,7 +17,7 @@ This repository contains the following components:
17 17
 |**Component**       |Description                                                                                                                                                                                         |
18 18
 |--------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
19 19
 | **registry**       | An implementation of the [Docker Registry HTTP API V2](docs/spec/api.md) for use with docker 1.6+.                                                                                                  |
20
-| **libraries**      | A rich set of libraries for interacting with,distribution components. Please see [godoc](https://godoc.org/github.com/docker/distribution) for details. **Note**: These libraries are **unstable**. |
20
+| **libraries**      | A rich set of libraries for interacting with distribution components. Please see [godoc](https://godoc.org/github.com/docker/distribution) for details. **Note**: These libraries are **unstable**. |
21 21
 | **specifications** | _Distribution_ related specifications are available in [docs/spec](docs/spec)                                                                                                                        |
22 22
 | **documentation**  | Docker's full documentation set is available at [docs.docker.com](https://docs.docker.com). This repository [contains the subset](docs/index.md) related just to the registry.                                                                                                                                          |
23 23
 
... ...
@@ -8,7 +8,7 @@ machine:
8 8
 
9 9
   post:
10 10
   # go
11
-    - gvm install go1.5.3 --prefer-binary --name=stable
11
+    - gvm install go1.6 --prefer-binary --name=stable
12 12
 
13 13
   environment:
14 14
   # Convenient shortcuts to "common" locations
... ...
@@ -49,26 +49,30 @@ test:
49 49
     # - gvm use old && go version
50 50
     - gvm use stable && go version
51 51
 
52
+  # Ensure validation of dependencies
53
+    - gvm use stable && if test -n "`git diff --stat=1000 master | grep -Ei \"vendor|godeps\"`"; then make dep-validate; fi:
54
+        pwd: $BASE_STABLE
55
+
52 56
   # First thing: build everything. This will catch compile errors, and it's
53 57
   # also necessary for go vet to work properly (see #807).
54
-    - gvm use stable && godep go install ./...:
58
+    - gvm use stable && godep go install $(go list ./... | grep -v "/vendor/"):
55 59
         pwd: $BASE_STABLE
56 60
 
57 61
   # FMT
58
-    - gvm use stable && test -z "$(gofmt -s -l . | grep -v Godeps/_workspace/src/ | tee /dev/stderr)":
62
+    - gvm use stable && make fmt:
59 63
         pwd: $BASE_STABLE
60 64
 
61 65
    # VET
62
-    - gvm use stable && go vet ./...:
66
+    - gvm use stable && make vet:
63 67
         pwd: $BASE_STABLE
64 68
 
65 69
   # LINT
66
-    - gvm use stable && test -z "$(golint ./... | grep -v Godeps/_workspace/src/ | tee /dev/stderr)":
70
+    - gvm use stable && make lint:
67 71
         pwd: $BASE_STABLE
68 72
 
69 73
   override:
70 74
   # Test stable, and report
71
-     - gvm use stable; export ROOT_PACKAGE=$(go list .); go list -tags "$DOCKER_BUILDTAGS" ./... | 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':
75
+     - 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':
72 76
          timeout: 600
73 77
          pwd: $BASE_STABLE
74 78
 
... ...
@@ -8,25 +8,17 @@ import (
8 8
 // since that time. If the key is not found, the value returned will be zero.
9 9
 // This is helpful when inferring metrics related to context execution times.
10 10
 func Since(ctx Context, key interface{}) time.Duration {
11
-	startedAtI := ctx.Value(key)
12
-	if startedAtI != nil {
13
-		if startedAt, ok := startedAtI.(time.Time); ok {
14
-			return time.Since(startedAt)
15
-		}
11
+	if startedAt, ok := ctx.Value(key).(time.Time); ok {
12
+		return time.Since(startedAt)
16 13
 	}
17
-
18 14
 	return 0
19 15
 }
20 16
 
21 17
 // GetStringValue returns a string value from the context. The empty string
22 18
 // will be returned if not found.
23 19
 func GetStringValue(ctx Context, key interface{}) (value string) {
24
-	stringi := ctx.Value(key)
25
-	if stringi != nil {
26
-		if valuev, ok := stringi.(string); ok {
27
-			value = valuev
28
-		}
20
+	if valuev, ok := ctx.Value(key).(string); ok {
21
+		value = valuev
29 22
 	}
30
-
31 23
 	return value
32 24
 }
... ...
@@ -3,5 +3,5 @@
3 3
 # need to be passed to `go test -coverpkg`:  this includes all of the
4 4
 # subpackage's dependencies within the containing package, as well as the
5 5
 # subpackage itself.
6
-DEPENDENCIES="$(go list -f $'{{range $f := .Deps}}{{$f}}\n{{end}}' ${1} | grep ${2})"
6
+DEPENDENCIES="$(go list -f $'{{range $f := .Deps}}{{$f}}\n{{end}}' ${1} | grep ${2} | grep -v github.com/docker/distribution/vendor)"
7 7
 echo "${1} ${DEPENDENCIES}" | xargs echo -n | tr ' ' ','
... ...
@@ -47,11 +47,19 @@ type PlatformSpec struct {
47 47
 	// OS specifies the operating system, for example `linux` or `windows`.
48 48
 	OS string `json:"os"`
49 49
 
50
+	// OSVersion is an optional field specifying the operating system
51
+	// version, for example `10.0.10586`.
52
+	OSVersion string `json:"os.version,omitempty"`
53
+
54
+	// OSFeatures is an optional field specifying an array of strings,
55
+	// each listing a required OS feature (for example on Windows `win32k`).
56
+	OSFeatures []string `json:"os.features,omitempty"`
57
+
50 58
 	// Variant is an optional field specifying a variant of the CPU, for
51 59
 	// example `ppc64le` to specify a little-endian version of a PowerPC CPU.
52 60
 	Variant string `json:"variant,omitempty"`
53 61
 
54
-	// Features is an optional field specifuing an array of strings, each
62
+	// Features is an optional field specifying an array of strings, each
55 63
 	// listing a required CPU feature (for example `sse4` or `aes`).
56 64
 	Features []string `json:"features,omitempty"`
57 65
 }
... ...
@@ -55,6 +55,9 @@ func (mb *builder) Build(ctx context.Context) (distribution.Manifest, error) {
55 55
 
56 56
 	// Add config to the blob store
57 57
 	m.Config, err = mb.bs.Put(ctx, MediaTypeConfig, mb.configJSON)
58
+	// Override MediaType, since Put always replaces the specified media
59
+	// type with application/octet-stream in the descriptor it returns.
60
+	m.Config.MediaType = MediaTypeConfig
58 61
 	if err != nil {
59 62
 		return nil, err
60 63
 	}
... ...
@@ -58,6 +58,20 @@ type ManifestServiceOption interface {
58 58
 	Apply(ManifestService) error
59 59
 }
60 60
 
61
+// WithTag allows a tag to be passed into Put
62
+func WithTag(tag string) ManifestServiceOption {
63
+	return WithTagOption{tag}
64
+}
65
+
66
+// WithTagOption holds a tag
67
+type WithTagOption struct{ Tag string }
68
+
69
+// Apply conforms to the ManifestServiceOption interface
70
+func (o WithTagOption) Apply(m ManifestService) error {
71
+	// no implementation
72
+	return nil
73
+}
74
+
61 75
 // Repository is a named collection of manifests and layers.
62 76
 type Repository interface {
63 77
 	// Named returns the name of the repository.
... ...
@@ -17,33 +17,35 @@ import (
17 17
 // under "/foo/v2/...". Most application will only provide a schema, host and
18 18
 // port, such as "https://localhost:5000/".
19 19
 type URLBuilder struct {
20
-	root   *url.URL // url root (ie http://localhost/)
21
-	router *mux.Router
20
+	root     *url.URL // url root (ie http://localhost/)
21
+	router   *mux.Router
22
+	relative bool
22 23
 }
23 24
 
24 25
 // NewURLBuilder creates a URLBuilder with provided root url object.
25
-func NewURLBuilder(root *url.URL) *URLBuilder {
26
+func NewURLBuilder(root *url.URL, relative bool) *URLBuilder {
26 27
 	return &URLBuilder{
27
-		root:   root,
28
-		router: Router(),
28
+		root:     root,
29
+		router:   Router(),
30
+		relative: relative,
29 31
 	}
30 32
 }
31 33
 
32 34
 // NewURLBuilderFromString workes identically to NewURLBuilder except it takes
33 35
 // a string argument for the root, returning an error if it is not a valid
34 36
 // url.
35
-func NewURLBuilderFromString(root string) (*URLBuilder, error) {
37
+func NewURLBuilderFromString(root string, relative bool) (*URLBuilder, error) {
36 38
 	u, err := url.Parse(root)
37 39
 	if err != nil {
38 40
 		return nil, err
39 41
 	}
40 42
 
41
-	return NewURLBuilder(u), nil
43
+	return NewURLBuilder(u, relative), nil
42 44
 }
43 45
 
44 46
 // NewURLBuilderFromRequest uses information from an *http.Request to
45 47
 // construct the root url.
46
-func NewURLBuilderFromRequest(r *http.Request) *URLBuilder {
48
+func NewURLBuilderFromRequest(r *http.Request, relative bool) *URLBuilder {
47 49
 	var scheme string
48 50
 
49 51
 	forwardedProto := r.Header.Get("X-Forwarded-Proto")
... ...
@@ -85,7 +87,7 @@ func NewURLBuilderFromRequest(r *http.Request) *URLBuilder {
85 85
 		u.Path = requestPath[0 : index+1]
86 86
 	}
87 87
 
88
-	return NewURLBuilder(u)
88
+	return NewURLBuilder(u, relative)
89 89
 }
90 90
 
91 91
 // BuildBaseURL constructs a base url for the API, typically just "/v2/".
... ...
@@ -194,12 +196,13 @@ func (ub *URLBuilder) cloneRoute(name string) clonedRoute {
194 194
 	*route = *ub.router.GetRoute(name) // clone the route
195 195
 	*root = *ub.root
196 196
 
197
-	return clonedRoute{Route: route, root: root}
197
+	return clonedRoute{Route: route, root: root, relative: ub.relative}
198 198
 }
199 199
 
200 200
 type clonedRoute struct {
201 201
 	*mux.Route
202
-	root *url.URL
202
+	root     *url.URL
203
+	relative bool
203 204
 }
204 205
 
205 206
 func (cr clonedRoute) URL(pairs ...string) (*url.URL, error) {
... ...
@@ -208,6 +211,10 @@ func (cr clonedRoute) URL(pairs ...string) (*url.URL, error) {
208 208
 		return nil, err
209 209
 	}
210 210
 
211
+	if cr.relative {
212
+		return routeURL, nil
213
+	}
214
+
211 215
 	if routeURL.Scheme == "" && routeURL.User == nil && routeURL.Host == "" {
212 216
 		routeURL.Path = routeURL.Path[1:]
213 217
 	}
... ...
@@ -62,7 +62,7 @@ func checkHTTPRedirect(req *http.Request, via []*http.Request) error {
62 62
 
63 63
 // NewRegistry creates a registry namespace which can be used to get a listing of repositories
64 64
 func NewRegistry(ctx context.Context, baseURL string, transport http.RoundTripper) (Registry, error) {
65
-	ub, err := v2.NewURLBuilderFromString(baseURL)
65
+	ub, err := v2.NewURLBuilderFromString(baseURL, false)
66 66
 	if err != nil {
67 67
 		return nil, err
68 68
 	}
... ...
@@ -133,7 +133,7 @@ func (r *registry) Repositories(ctx context.Context, entries []string, last stri
133 133
 
134 134
 // NewRepository creates a new Repository for the given repository name and base URL.
135 135
 func NewRepository(ctx context.Context, name reference.Named, baseURL string, transport http.RoundTripper) (distribution.Repository, error) {
136
-	ub, err := v2.NewURLBuilderFromString(baseURL)
136
+	ub, err := v2.NewURLBuilderFromString(baseURL, false)
137 137
 	if err != nil {
138 138
 		return nil, err
139 139
 	}
... ...
@@ -402,9 +402,9 @@ func (ms *manifests) Get(ctx context.Context, dgst digest.Digest, options ...dis
402 402
 	)
403 403
 
404 404
 	for _, option := range options {
405
-		if opt, ok := option.(withTagOption); ok {
406
-			digestOrTag = opt.tag
407
-			ref, err = reference.WithTag(ms.name, opt.tag)
405
+		if opt, ok := option.(distribution.WithTagOption); ok {
406
+			digestOrTag = opt.Tag
407
+			ref, err = reference.WithTag(ms.name, opt.Tag)
408 408
 			if err != nil {
409 409
 				return nil, err
410 410
 			}
... ...
@@ -465,21 +465,6 @@ func (ms *manifests) Get(ctx context.Context, dgst digest.Digest, options ...dis
465 465
 	return nil, HandleErrorResponse(resp)
466 466
 }
467 467
 
468
-// WithTag allows a tag to be passed into Put which enables the client
469
-// to build a correct URL.
470
-func WithTag(tag string) distribution.ManifestServiceOption {
471
-	return withTagOption{tag}
472
-}
473
-
474
-type withTagOption struct{ tag string }
475
-
476
-func (o withTagOption) Apply(m distribution.ManifestService) error {
477
-	if _, ok := m.(*manifests); ok {
478
-		return nil
479
-	}
480
-	return fmt.Errorf("withTagOption is a client-only option")
481
-}
482
-
483 468
 // Put puts a manifest.  A tag can be specified using an options parameter which uses some shared state to hold the
484 469
 // tag name in order to build the correct upload URL.
485 470
 func (ms *manifests) Put(ctx context.Context, m distribution.Manifest, options ...distribution.ManifestServiceOption) (digest.Digest, error) {
... ...
@@ -487,9 +472,9 @@ func (ms *manifests) Put(ctx context.Context, m distribution.Manifest, options .
487 487
 	var tagged bool
488 488
 
489 489
 	for _, option := range options {
490
-		if opt, ok := option.(withTagOption); ok {
490
+		if opt, ok := option.(distribution.WithTagOption); ok {
491 491
 			var err error
492
-			ref, err = reference.WithTag(ref, opt.tag)
492
+			ref, err = reference.WithTag(ref, opt.Tag)
493 493
 			if err != nil {
494 494
 				return "", err
495 495
 			}