Browse code

Support layers from external URLs

This is used to support downloading Windows base images from Microsoft
servers.

Signed-off-by: John Starks <jostarks@microsoft.com>

John Starks authored on 2016/05/26 11:11:51
Showing 16 changed files
... ...
@@ -133,6 +133,7 @@ type v2LayerDescriptor struct {
133 133
 	V2MetadataService *metadata.V2MetadataService
134 134
 	tmpFile           *os.File
135 135
 	verifier          digest.Verifier
136
+	foreignSrc        *distribution.Descriptor
136 137
 }
137 138
 
138 139
 func (ld *v2LayerDescriptor) Key() string {
... ...
@@ -180,9 +181,8 @@ func (ld *v2LayerDescriptor) Download(ctx context.Context, progressOutput progre
180 180
 	}
181 181
 
182 182
 	tmpFile := ld.tmpFile
183
-	blobs := ld.repo.Blobs(ctx)
184 183
 
185
-	layerDownload, err := blobs.Open(ctx, ld.digest)
184
+	layerDownload, err := ld.open(ctx)
186 185
 	if err != nil {
187 186
 		logrus.Errorf("Error initiating layer download: %v", err)
188 187
 		if err == distribution.ErrBlobUnknown {
... ...
@@ -501,6 +501,29 @@ func (p *v2Puller) pullSchema2(ctx context.Context, ref reference.Named, mfst *s
501 501
 		return imageID, manifestDigest, nil
502 502
 	}
503 503
 
504
+	var descriptors []xfer.DownloadDescriptor
505
+
506
+	// Note that the order of this loop is in the direction of bottom-most
507
+	// to top-most, so that the downloads slice gets ordered correctly.
508
+	for _, d := range mfst.Layers {
509
+		layerDescriptor := &v2LayerDescriptor{
510
+			digest:            d.Digest,
511
+			repo:              p.repo,
512
+			repoInfo:          p.repoInfo,
513
+			V2MetadataService: p.V2MetadataService,
514
+		}
515
+
516
+		if d.MediaType == schema2.MediaTypeForeignLayer && len(d.URLs) > 0 {
517
+			if !layer.ForeignSourceSupported() {
518
+				return "", "", errors.New("foreign layers are not supported on this OS")
519
+			}
520
+
521
+			layerDescriptor.foreignSrc = &d
522
+		}
523
+
524
+		descriptors = append(descriptors, layerDescriptor)
525
+	}
526
+
504 527
 	configChan := make(chan []byte, 1)
505 528
 	errChan := make(chan error, 1)
506 529
 	var cancel func()
... ...
@@ -517,21 +540,6 @@ func (p *v2Puller) pullSchema2(ctx context.Context, ref reference.Named, mfst *s
517 517
 		configChan <- configJSON
518 518
 	}()
519 519
 
520
-	var descriptors []xfer.DownloadDescriptor
521
-
522
-	// Note that the order of this loop is in the direction of bottom-most
523
-	// to top-most, so that the downloads slice gets ordered correctly.
524
-	for _, d := range mfst.References() {
525
-		layerDescriptor := &v2LayerDescriptor{
526
-			digest:            d.Digest,
527
-			repo:              p.repo,
528
-			repoInfo:          p.repoInfo,
529
-			V2MetadataService: p.V2MetadataService,
530
-		}
531
-
532
-		descriptors = append(descriptors, layerDescriptor)
533
-	}
534
-
535 520
 	var (
536 521
 		configJSON         []byte       // raw serialized image config
537 522
 		unmarshalledConfig image.Image  // deserialized image config
... ...
@@ -3,6 +3,8 @@
3 3
 package distribution
4 4
 
5 5
 import (
6
+	"github.com/docker/distribution"
7
+	"github.com/docker/distribution/context"
6 8
 	"github.com/docker/distribution/manifest/schema1"
7 9
 	"github.com/docker/docker/image"
8 10
 )
... ...
@@ -10,3 +12,8 @@ import (
10 10
 func detectBaseLayer(is image.Store, m *schema1.Manifest, rootFS *image.RootFS) error {
11 11
 	return nil
12 12
 }
13
+
14
+func (ld *v2LayerDescriptor) open(ctx context.Context) (distribution.ReadSeekCloser, error) {
15
+	blobs := ld.repo.Blobs(ctx)
16
+	return blobs.Open(ctx, ld.digest)
17
+}
... ...
@@ -5,9 +5,15 @@ package distribution
5 5
 import (
6 6
 	"encoding/json"
7 7
 	"fmt"
8
+	"net/http"
9
+	"os"
8 10
 
11
+	"github.com/docker/distribution"
12
+	"github.com/docker/distribution/context"
9 13
 	"github.com/docker/distribution/manifest/schema1"
14
+	"github.com/docker/distribution/registry/client/transport"
10 15
 	"github.com/docker/docker/image"
16
+	"github.com/docker/docker/layer"
11 17
 )
12 18
 
13 19
 func detectBaseLayer(is image.Store, m *schema1.Manifest, rootFS *image.RootFS) error {
... ...
@@ -28,3 +34,33 @@ func detectBaseLayer(is image.Store, m *schema1.Manifest, rootFS *image.RootFS)
28 28
 	}
29 29
 	return fmt.Errorf("Invalid base layer %q", v1img.Parent)
30 30
 }
31
+
32
+var _ layer.ForeignSourcer = &v2LayerDescriptor{}
33
+
34
+func (ld *v2LayerDescriptor) ForeignSource() *distribution.Descriptor {
35
+	return ld.foreignSrc
36
+}
37
+
38
+func (ld *v2LayerDescriptor) open(ctx context.Context) (distribution.ReadSeekCloser, error) {
39
+	if ld.foreignSrc == nil {
40
+		blobs := ld.repo.Blobs(ctx)
41
+		return blobs.Open(ctx, ld.digest)
42
+	}
43
+
44
+	var (
45
+		err error
46
+		rsc distribution.ReadSeekCloser
47
+	)
48
+
49
+	// Find the first URL that results in a 200 result code.
50
+	for _, url := range ld.foreignSrc.URLs {
51
+		rsc = transport.NewHTTPReadSeeker(http.DefaultClient, url, nil)
52
+		_, err = rsc.Seek(0, os.SEEK_SET)
53
+		if err == nil {
54
+			break
55
+		}
56
+		rsc.Close()
57
+		rsc = nil
58
+	}
59
+	return rsc, err
60
+}
... ...
@@ -240,6 +240,13 @@ func (pd *v2PushDescriptor) DiffID() layer.DiffID {
240 240
 }
241 241
 
242 242
 func (pd *v2PushDescriptor) Upload(ctx context.Context, progressOutput progress.Output) (distribution.Descriptor, error) {
243
+	if fs, ok := pd.layer.(layer.ForeignSourcer); ok {
244
+		if d := fs.ForeignSource(); d != nil {
245
+			progress.Update(progressOutput, pd.ID(), "Skipped foreign layer")
246
+			return *d, nil
247
+		}
248
+	}
249
+
243 250
 	diffID := pd.DiffID()
244 251
 
245 252
 	pd.pushState.Lock()
... ...
@@ -7,6 +7,7 @@ import (
7 7
 	"time"
8 8
 
9 9
 	"github.com/Sirupsen/logrus"
10
+	"github.com/docker/distribution"
10 11
 	"github.com/docker/docker/image"
11 12
 	"github.com/docker/docker/layer"
12 13
 	"github.com/docker/docker/pkg/archive"
... ...
@@ -318,7 +319,11 @@ func (ldm *LayerDownloadManager) makeDownloadFunc(descriptor DownloadDescriptor,
318 318
 				return
319 319
 			}
320 320
 
321
-			d.layer, err = d.layerStore.Register(inflatedLayerData, parentLayer)
321
+			var src *distribution.Descriptor
322
+			if fs, ok := descriptor.(layer.ForeignSourcer); ok {
323
+				src = fs.ForeignSource()
324
+			}
325
+			d.layer, err = d.layerStore.RegisterForeign(inflatedLayerData, parentLayer, src)
322 326
 			if err != nil {
323 327
 				select {
324 328
 				case <-d.Transfer.Context().Done():
... ...
@@ -409,7 +414,11 @@ func (ldm *LayerDownloadManager) makeDownloadFuncFromDownload(descriptor Downloa
409 409
 			}
410 410
 			defer layerReader.Close()
411 411
 
412
-			d.layer, err = d.layerStore.Register(layerReader, parentLayer)
412
+			var src *distribution.Descriptor
413
+			if fs, ok := l.(layer.ForeignSourcer); ok {
414
+				src = fs.ForeignSource()
415
+			}
416
+			d.layer, err = d.layerStore.RegisterForeign(layerReader, parentLayer, src)
413 417
 			if err != nil {
414 418
 				d.err = fmt.Errorf("failed to register layer: %v", err)
415 419
 				return
... ...
@@ -10,6 +10,7 @@ import (
10 10
 	"testing"
11 11
 	"time"
12 12
 
13
+	"github.com/docker/distribution"
13 14
 	"github.com/docker/distribution/digest"
14 15
 	"github.com/docker/docker/image"
15 16
 	"github.com/docker/docker/layer"
... ...
@@ -71,6 +72,10 @@ func createChainIDFromParent(parent layer.ChainID, dgsts ...layer.DiffID) layer.
71 71
 }
72 72
 
73 73
 func (ls *mockLayerStore) Register(reader io.Reader, parentID layer.ChainID) (layer.Layer, error) {
74
+	return ls.RegisterForeign(reader, parentID, nil)
75
+}
76
+
77
+func (ls *mockLayerStore) RegisterForeign(reader io.Reader, parentID layer.ChainID, _ *distribution.Descriptor) (layer.Layer, error) {
74 78
 	var (
75 79
 		parent layer.Layer
76 80
 		err    error
... ...
@@ -10,6 +10,7 @@ import (
10 10
 	"reflect"
11 11
 
12 12
 	"github.com/Sirupsen/logrus"
13
+	"github.com/docker/distribution"
13 14
 	"github.com/docker/docker/image"
14 15
 	"github.com/docker/docker/image/v1"
15 16
 	"github.com/docker/docker/layer"
... ...
@@ -63,6 +64,10 @@ func (l *tarexporter) Load(inTar io.ReadCloser, outStream io.Writer, quiet bool)
63 63
 	var parentLinks []parentLink
64 64
 
65 65
 	for _, m := range manifest {
66
+		if m.LayerSources != nil && !layer.ForeignSourceSupported() {
67
+			return fmt.Errorf("invalid manifest, foreign layers not supported on this operating system")
68
+		}
69
+
66 70
 		configPath, err := safePath(tmpDir, m.Config)
67 71
 		if err != nil {
68 72
 			return err
... ...
@@ -92,7 +97,7 @@ func (l *tarexporter) Load(inTar io.ReadCloser, outStream io.Writer, quiet bool)
92 92
 			r.Append(diffID)
93 93
 			newLayer, err := l.ls.Get(r.ChainID())
94 94
 			if err != nil {
95
-				newLayer, err = l.loadLayer(layerPath, rootFS, diffID.String(), progressOutput)
95
+				newLayer, err = l.loadLayer(layerPath, rootFS, diffID.String(), m.LayerSources[diffID], progressOutput)
96 96
 				if err != nil {
97 97
 					return err
98 98
 				}
... ...
@@ -151,7 +156,7 @@ func (l *tarexporter) setParentID(id, parentID image.ID) error {
151 151
 	return l.is.SetParent(id, parentID)
152 152
 }
153 153
 
154
-func (l *tarexporter) loadLayer(filename string, rootFS image.RootFS, id string, progressOutput progress.Output) (layer.Layer, error) {
154
+func (l *tarexporter) loadLayer(filename string, rootFS image.RootFS, id string, foreignSrc *distribution.Descriptor, progressOutput progress.Output) (layer.Layer, error) {
155 155
 	rawTar, err := os.Open(filename)
156 156
 	if err != nil {
157 157
 		logrus.Debugf("Error reading embedded tar: %v", err)
... ...
@@ -174,9 +179,9 @@ func (l *tarexporter) loadLayer(filename string, rootFS image.RootFS, id string,
174 174
 
175 175
 		progressReader := progress.NewProgressReader(inflatedLayerData, progressOutput, fileInfo.Size(), stringid.TruncateID(id), "Loading layer")
176 176
 
177
-		return l.ls.Register(progressReader, rootFS.ChainID())
177
+		return l.ls.RegisterForeign(progressReader, rootFS.ChainID(), foreignSrc)
178 178
 	}
179
-	return l.ls.Register(inflatedLayerData, rootFS.ChainID())
179
+	return l.ls.RegisterForeign(inflatedLayerData, rootFS.ChainID(), foreignSrc)
180 180
 }
181 181
 
182 182
 func (l *tarexporter) setLoadedTag(ref reference.NamedTagged, imgID image.ID, outStream io.Writer) error {
... ...
@@ -298,7 +303,7 @@ func (l *tarexporter) legacyLoadImage(oldID, sourceDir string, loadedMap map[str
298 298
 	if err != nil {
299 299
 		return err
300 300
 	}
301
-	newLayer, err := l.loadLayer(layerPath, *rootFS, oldID, progressOutput)
301
+	newLayer, err := l.loadLayer(layerPath, *rootFS, oldID, nil, progressOutput)
302 302
 	if err != nil {
303 303
 		return err
304 304
 	}
... ...
@@ -9,6 +9,7 @@ import (
9 9
 	"path/filepath"
10 10
 	"time"
11 11
 
12
+	"github.com/docker/distribution"
12 13
 	"github.com/docker/distribution/digest"
13 14
 	"github.com/docker/docker/image"
14 15
 	"github.com/docker/docker/image/v1"
... ...
@@ -131,7 +132,8 @@ func (s *saveSession) save(outStream io.Writer) error {
131 131
 	var parentLinks []parentLink
132 132
 
133 133
 	for id, imageDescr := range s.images {
134
-		if err = s.saveImage(id); err != nil {
134
+		foreignSrcs, err := s.saveImage(id)
135
+		if err != nil {
135 136
 			return err
136 137
 		}
137 138
 
... ...
@@ -151,9 +153,10 @@ func (s *saveSession) save(outStream io.Writer) error {
151 151
 		}
152 152
 
153 153
 		manifest = append(manifest, manifestItem{
154
-			Config:   digest.Digest(id).Hex() + ".json",
155
-			RepoTags: repoTags,
156
-			Layers:   layers,
154
+			Config:       digest.Digest(id).Hex() + ".json",
155
+			RepoTags:     repoTags,
156
+			Layers:       layers,
157
+			LayerSources: foreignSrcs,
157 158
 		})
158 159
 
159 160
 		parentID, _ := s.is.GetParent(id)
... ...
@@ -213,18 +216,19 @@ func (s *saveSession) save(outStream io.Writer) error {
213 213
 	return nil
214 214
 }
215 215
 
216
-func (s *saveSession) saveImage(id image.ID) error {
216
+func (s *saveSession) saveImage(id image.ID) (map[layer.DiffID]*distribution.Descriptor, error) {
217 217
 	img, err := s.is.Get(id)
218 218
 	if err != nil {
219
-		return err
219
+		return nil, err
220 220
 	}
221 221
 
222 222
 	if len(img.RootFS.DiffIDs) == 0 {
223
-		return fmt.Errorf("empty export - not implemented")
223
+		return nil, fmt.Errorf("empty export - not implemented")
224 224
 	}
225 225
 
226 226
 	var parent digest.Digest
227 227
 	var layers []string
228
+	var foreignSrcs map[layer.DiffID]*distribution.Descriptor
228 229
 	for i := range img.RootFS.DiffIDs {
229 230
 		v1Img := image.V1Image{}
230 231
 		if i == len(img.RootFS.DiffIDs)-1 {
... ...
@@ -234,7 +238,7 @@ func (s *saveSession) saveImage(id image.ID) error {
234 234
 		rootFS.DiffIDs = rootFS.DiffIDs[:i+1]
235 235
 		v1ID, err := v1.CreateID(v1Img, rootFS.ChainID(), parent)
236 236
 		if err != nil {
237
-			return err
237
+			return nil, err
238 238
 		}
239 239
 
240 240
 		v1Img.ID = v1ID.Hex()
... ...
@@ -242,79 +246,91 @@ func (s *saveSession) saveImage(id image.ID) error {
242 242
 			v1Img.Parent = parent.Hex()
243 243
 		}
244 244
 
245
-		if err := s.saveLayer(rootFS.ChainID(), v1Img, img.Created); err != nil {
246
-			return err
245
+		src, err := s.saveLayer(rootFS.ChainID(), v1Img, img.Created)
246
+		if err != nil {
247
+			return nil, err
247 248
 		}
248 249
 		layers = append(layers, v1Img.ID)
249 250
 		parent = v1ID
251
+		if src != nil {
252
+			if foreignSrcs == nil {
253
+				foreignSrcs = make(map[layer.DiffID]*distribution.Descriptor)
254
+			}
255
+			foreignSrcs[img.RootFS.DiffIDs[i]] = src
256
+		}
250 257
 	}
251 258
 
252 259
 	configFile := filepath.Join(s.outDir, digest.Digest(id).Hex()+".json")
253 260
 	if err := ioutil.WriteFile(configFile, img.RawJSON(), 0644); err != nil {
254
-		return err
261
+		return nil, err
255 262
 	}
256 263
 	if err := system.Chtimes(configFile, img.Created, img.Created); err != nil {
257
-		return err
264
+		return nil, err
258 265
 	}
259 266
 
260 267
 	s.images[id].layers = layers
261
-	return nil
268
+	return foreignSrcs, nil
262 269
 }
263 270
 
264
-func (s *saveSession) saveLayer(id layer.ChainID, legacyImg image.V1Image, createdTime time.Time) error {
271
+func (s *saveSession) saveLayer(id layer.ChainID, legacyImg image.V1Image, createdTime time.Time) (*distribution.Descriptor, error) {
265 272
 	if _, exists := s.savedLayers[legacyImg.ID]; exists {
266
-		return nil
273
+		return nil, nil
267 274
 	}
268 275
 
269 276
 	outDir := filepath.Join(s.outDir, legacyImg.ID)
270 277
 	if err := os.Mkdir(outDir, 0755); err != nil {
271
-		return err
278
+		return nil, err
272 279
 	}
273 280
 
274 281
 	// todo: why is this version file here?
275 282
 	if err := ioutil.WriteFile(filepath.Join(outDir, legacyVersionFileName), []byte("1.0"), 0644); err != nil {
276
-		return err
283
+		return nil, err
277 284
 	}
278 285
 
279 286
 	imageConfig, err := json.Marshal(legacyImg)
280 287
 	if err != nil {
281
-		return err
288
+		return nil, err
282 289
 	}
283 290
 
284 291
 	if err := ioutil.WriteFile(filepath.Join(outDir, legacyConfigFileName), imageConfig, 0644); err != nil {
285
-		return err
292
+		return nil, err
286 293
 	}
287 294
 
288 295
 	// serialize filesystem
289 296
 	tarFile, err := os.Create(filepath.Join(outDir, legacyLayerFileName))
290 297
 	if err != nil {
291
-		return err
298
+		return nil, err
292 299
 	}
293 300
 	defer tarFile.Close()
294 301
 
295 302
 	l, err := s.ls.Get(id)
296 303
 	if err != nil {
297
-		return err
304
+		return nil, err
298 305
 	}
299 306
 	defer layer.ReleaseAndLog(s.ls, l)
300 307
 
301 308
 	arch, err := l.TarStream()
302 309
 	if err != nil {
303
-		return err
310
+		return nil, err
304 311
 	}
305 312
 	defer arch.Close()
306 313
 
307 314
 	if _, err := io.Copy(tarFile, arch); err != nil {
308
-		return err
315
+		return nil, err
309 316
 	}
310 317
 
311 318
 	for _, fname := range []string{"", legacyVersionFileName, legacyConfigFileName, legacyLayerFileName} {
312 319
 		// todo: maybe save layer created timestamp?
313 320
 		if err := system.Chtimes(filepath.Join(outDir, fname), createdTime, createdTime); err != nil {
314
-			return err
321
+			return nil, err
315 322
 		}
316 323
 	}
317 324
 
318 325
 	s.savedLayers[legacyImg.ID] = struct{}{}
319
-	return nil
326
+
327
+	var src *distribution.Descriptor
328
+	if fs, ok := l.(layer.ForeignSourcer); ok {
329
+		src = fs.ForeignSource()
330
+	}
331
+	return src, nil
320 332
 }
... ...
@@ -1,6 +1,7 @@
1 1
 package tarexport
2 2
 
3 3
 import (
4
+	"github.com/docker/distribution"
4 5
 	"github.com/docker/docker/image"
5 6
 	"github.com/docker/docker/layer"
6 7
 	"github.com/docker/docker/reference"
... ...
@@ -15,10 +16,11 @@ const (
15 15
 )
16 16
 
17 17
 type manifestItem struct {
18
-	Config   string
19
-	RepoTags []string
20
-	Layers   []string
21
-	Parent   image.ID `json:",omitempty"`
18
+	Config       string
19
+	RepoTags     []string
20
+	Layers       []string
21
+	Parent       image.ID                                  `json:",omitempty"`
22
+	LayerSources map[layer.DiffID]*distribution.Descriptor `json:",omitempty"`
22 23
 }
23 24
 
24 25
 type tarexporter struct {
... ...
@@ -2,6 +2,7 @@ package layer
2 2
 
3 3
 import (
4 4
 	"compress/gzip"
5
+	"encoding/json"
5 6
 	"errors"
6 7
 	"fmt"
7 8
 	"io"
... ...
@@ -13,6 +14,7 @@ import (
13 13
 	"strings"
14 14
 
15 15
 	"github.com/Sirupsen/logrus"
16
+	"github.com/docker/distribution"
16 17
 	"github.com/docker/distribution/digest"
17 18
 	"github.com/docker/docker/pkg/ioutils"
18 19
 )
... ...
@@ -24,6 +26,9 @@ var (
24 24
 		// digest.SHA384, // Currently not used
25 25
 		// digest.SHA512, // Currently not used
26 26
 	}
27
+
28
+	// ErrNoForeignSource is returned when no foreign source is set for a layer.
29
+	ErrNoForeignSource = errors.New("layer does not have a foreign source")
27 30
 )
28 31
 
29 32
 type fileMetadataStore struct {
... ...
@@ -98,6 +103,14 @@ func (fm *fileMetadataTransaction) SetCacheID(cacheID string) error {
98 98
 	return ioutil.WriteFile(filepath.Join(fm.root, "cache-id"), []byte(cacheID), 0644)
99 99
 }
100 100
 
101
+func (fm *fileMetadataTransaction) SetForeignSource(ref distribution.Descriptor) error {
102
+	jsonRef, err := json.Marshal(ref)
103
+	if err != nil {
104
+		return err
105
+	}
106
+	return ioutil.WriteFile(filepath.Join(fm.root, "descriptor.json"), jsonRef, 0644)
107
+}
108
+
101 109
 func (fm *fileMetadataTransaction) TarSplitWriter(compressInput bool) (io.WriteCloser, error) {
102 110
 	f, err := os.OpenFile(filepath.Join(fm.root, "tar-split.json.gz"), os.O_TRUNC|os.O_CREATE|os.O_WRONLY, 0644)
103 111
 	if err != nil {
... ...
@@ -191,6 +204,23 @@ func (fms *fileMetadataStore) GetCacheID(layer ChainID) (string, error) {
191 191
 	return content, nil
192 192
 }
193 193
 
194
+func (fms *fileMetadataStore) GetForeignSource(layer ChainID) (distribution.Descriptor, error) {
195
+	content, err := ioutil.ReadFile(fms.getLayerFilename(layer, "descriptor.json"))
196
+	if err != nil {
197
+		if os.IsNotExist(err) {
198
+			return distribution.Descriptor{}, ErrNoForeignSource
199
+		}
200
+		return distribution.Descriptor{}, err
201
+	}
202
+
203
+	var ref distribution.Descriptor
204
+	err = json.Unmarshal(content, &ref)
205
+	if err != nil {
206
+		return distribution.Descriptor{}, err
207
+	}
208
+	return ref, err
209
+}
210
+
194 211
 func (fms *fileMetadataStore) TarSplitReader(layer ChainID) (io.ReadCloser, error) {
195 212
 	fz, err := os.Open(fms.getLayerFilename(layer, "tar-split.json.gz"))
196 213
 	if err != nil {
... ...
@@ -14,6 +14,7 @@ import (
14 14
 	"io"
15 15
 
16 16
 	"github.com/Sirupsen/logrus"
17
+	"github.com/docker/distribution"
17 18
 	"github.com/docker/distribution/digest"
18 19
 	"github.com/docker/docker/pkg/archive"
19 20
 )
... ...
@@ -107,6 +108,14 @@ type Layer interface {
107 107
 	Metadata() (map[string]string, error)
108 108
 }
109 109
 
110
+// ForeignSourcer is an interface used to describe the source of layers
111
+// and objects representing layers, when the source is a foreign URL.
112
+type ForeignSourcer interface {
113
+	// ForeignSource returns the descriptor for this layer if it is
114
+	// a foreign layer, or nil for ordinary layers.
115
+	ForeignSource() *distribution.Descriptor
116
+}
117
+
110 118
 // RWLayer represents a layer which is
111 119
 // read and writable
112 120
 type RWLayer interface {
... ...
@@ -168,6 +177,7 @@ type MountInit func(root string) error
168 168
 // read-only and read-write layers.
169 169
 type Store interface {
170 170
 	Register(io.Reader, ChainID) (Layer, error)
171
+	RegisterForeign(io.Reader, ChainID, *distribution.Descriptor) (Layer, error)
171 172
 	Get(ChainID) (Layer, error)
172 173
 	Release(Layer) ([]Metadata, error)
173 174
 
... ...
@@ -189,6 +199,7 @@ type MetadataTransaction interface {
189 189
 	SetParent(parent ChainID) error
190 190
 	SetDiffID(DiffID) error
191 191
 	SetCacheID(string) error
192
+	SetForeignSource(distribution.Descriptor) error
192 193
 	TarSplitWriter(compressInput bool) (io.WriteCloser, error)
193 194
 
194 195
 	Commit(ChainID) error
... ...
@@ -208,6 +219,7 @@ type MetadataStore interface {
208 208
 	GetParent(ChainID) (ChainID, error)
209 209
 	GetDiffID(ChainID) (DiffID, error)
210 210
 	GetCacheID(ChainID) (string, error)
211
+	GetForeignSource(ChainID) (distribution.Descriptor, error)
211 212
 	TarSplitReader(ChainID) (io.ReadCloser, error)
212 213
 
213 214
 	SetMountID(string, string) error
... ...
@@ -8,6 +8,7 @@ import (
8 8
 	"sync"
9 9
 
10 10
 	"github.com/Sirupsen/logrus"
11
+	"github.com/docker/distribution"
11 12
 	"github.com/docker/distribution/digest"
12 13
 	"github.com/docker/docker/daemon/graphdriver"
13 14
 	"github.com/docker/docker/pkg/archive"
... ...
@@ -137,6 +138,13 @@ func (ls *layerStore) loadLayer(layer ChainID) (*roLayer, error) {
137 137
 		references: map[Layer]struct{}{},
138 138
 	}
139 139
 
140
+	foreignSrc, err := ls.store.GetForeignSource(layer)
141
+	if err == nil {
142
+		cl.foreignSrc = &foreignSrc
143
+	} else if err != ErrNoForeignSource {
144
+		return nil, fmt.Errorf("failed to get foreign reference for %s: %s", layer, err)
145
+	}
146
+
140 147
 	if parent != "" {
141 148
 		p, err := ls.loadLayer(parent)
142 149
 		if err != nil {
... ...
@@ -228,6 +236,10 @@ func (ls *layerStore) applyTar(tx MetadataTransaction, ts io.Reader, parent stri
228 228
 }
229 229
 
230 230
 func (ls *layerStore) Register(ts io.Reader, parent ChainID) (Layer, error) {
231
+	return ls.RegisterForeign(ts, parent, nil)
232
+}
233
+
234
+func (ls *layerStore) RegisterForeign(ts io.Reader, parent ChainID, foreignSrc *distribution.Descriptor) (Layer, error) {
231 235
 	// err is used to hold the error which will always trigger
232 236
 	// cleanup of creates sources but may not be an error returned
233 237
 	// to the caller (already exists).
... ...
@@ -258,6 +270,7 @@ func (ls *layerStore) Register(ts io.Reader, parent ChainID) (Layer, error) {
258 258
 	layer := &roLayer{
259 259
 		parent:         p,
260 260
 		cacheID:        stringid.GenerateRandomID(),
261
+		foreignSrc:     foreignSrc,
261 262
 		referenceCount: 1,
262 263
 		layerStore:     ls,
263 264
 		references:     map[Layer]struct{}{},
... ...
@@ -7,3 +7,9 @@ import "github.com/docker/docker/pkg/stringid"
7 7
 func (ls *layerStore) mountID(name string) string {
8 8
 	return stringid.GenerateRandomID()
9 9
 }
10
+
11
+// ForeignSourceSupported returns whether layers downloaded from foreign sources are
12
+// supported in this daemon.
13
+func ForeignSourceSupported() bool {
14
+	return false
15
+}
... ...
@@ -96,3 +96,9 @@ func (ls *layerStore) mountID(name string) string {
96 96
 func (ls *layerStore) GraphDriver() graphdriver.Driver {
97 97
 	return ls.driver
98 98
 }
99
+
100
+// ForeignSourceSupported returns whether layers downloaded from foreign sources are
101
+// supported in this daemon.
102
+func ForeignSourceSupported() bool {
103
+	return true
104
+}
... ...
@@ -4,6 +4,7 @@ import (
4 4
 	"fmt"
5 5
 	"io"
6 6
 
7
+	"github.com/docker/distribution"
7 8
 	"github.com/docker/distribution/digest"
8 9
 )
9 10
 
... ...
@@ -14,6 +15,7 @@ type roLayer struct {
14 14
 	cacheID    string
15 15
 	size       int64
16 16
 	layerStore *layerStore
17
+	foreignSrc *distribution.Descriptor
17 18
 
18 19
 	referenceCount int
19 20
 	references     map[Layer]struct{}
... ...
@@ -123,6 +125,11 @@ func storeLayer(tx MetadataTransaction, layer *roLayer) error {
123 123
 			return err
124 124
 		}
125 125
 	}
126
+	if layer.foreignSrc != nil {
127
+		if err := tx.SetForeignSource(*layer.foreignSrc); err != nil {
128
+			return err
129
+		}
130
+	}
126 131
 
127 132
 	return nil
128 133
 }
129 134
new file mode 100644
... ...
@@ -0,0 +1,9 @@
0
+package layer
1
+
2
+import "github.com/docker/distribution"
3
+
4
+var _ ForeignSourcer = &roLayer{}
5
+
6
+func (rl *roLayer) ForeignSource() *distribution.Descriptor {
7
+	return rl.foreignSrc
8
+}